CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PIC24 - Sampling/clock timer

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
sd_tom



Joined: 06 Feb 2018
Posts: 3

View user's profile Send private message

PIC24 - Sampling/clock timer
PostPosted: Tue Feb 06, 2018 10:38 am     Reply with quote

I've looked through several of the equations on converting timer to wall time, and have composed the equations serveral ways, getting preloads that make sense.. so there's obviously something I'm missing here.. DIV_8 works, DIV_64 or DIV_256 run too slow. Is my calculation totally wrong and by some dumb luck DIV_8 is working but for the wrong reasons?

The Fosc/2 is a PIC24 thing (seeing most examples is /4)
Timer 3 - Type C timer, which is the back 16-bit of a 32-bit timer ;

Code:

#include <24EP512GU814.h>
#device ADC = 8

// Setup delay_us/delay_ms
#use delay(crystal = 8 Mhz, clock = 128 Mhz)

// Debug UART
#pin_select U1TX = PIN_F3
#pin_select U1RX = PIN_A3
#use rs232(UART1, baud = 115200, stream = STREAM_CONS)

unsigned int16 clock_TimerValue;
unsigned long clock_gticks;
unsigned long clock_tickHz;

#define RATEHZ 1000
#define ONE_HZ (RATEHZ)
#define FIFTY_HZ (RATEHZ / 50)
#define TEN_HZ (RATEHZ / 10)

#int_timer3
void timer_tick(void)
{
    // timer keeps going so account for majority of delays
    //  - not perfect since these instructions themselves take time
    set_timer3( get_timer3() + clock_TimerValue );

    clock_gticks++;
    // 1 sec printout
    if ((clock_gticks % 1000) == 0)
    {
        output_toggle(PIN_H0);
        fprintf(STREAM_CONS, "ticks: %d\r\n", clock_gticks);
    }
}

//==========================
#zero_ram
void main()
{
    // Create 1000hz clock (1ms ticks)
    clock_tickHz = 1000; //hz

#if 1
    // WORKS
    clock_TimerValue = 65536 - (128000000/2/8/clock_tickHz); // Fosc/2 / DIV / frequency
    setup_timer3(TMR_INTERNAL | TMR_DIV_BY_8);
#else
    // Doesn't work - Runs many x slower
    clock_TimerValue = 65536 - (128000000/2/64/clock_tickHz); // Fosc/2 / DIV / frequency
    setup_timer3(TMR_INTERNAL | TMR_DIV_BY_64);
#endif

    set_timer3(clock_TimerValue);
   
    enable_interrupts(INT_TIMER3);
    enable_interrupts(GLOBAL);

    while (TRUE)
    {
    }
}


And yes, the printout on 1sec marks throws it off but we're not at that level of granularity where that's making a difference
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Feb 06, 2018 10:46 am     Reply with quote

While I don't use that PIC I'll offer 2 ideas.

1) printout the 'clock signal path' or whatever it's called just to be sure the choice you've chosen is correct...

2) have a look at the 'software RTC' program in the 'code library'. It may offer you 'insight'. At least I know it works for 16 and 18 series PICs.

Jay
newguy



Joined: 24 Jun 2004
Posts: 1907

View user's profile Send private message

PostPosted: Tue Feb 06, 2018 10:53 am     Reply with quote

Didn't really dig too hard but my "casting spidey sense" is going off. Why can't you replace the math with #defines?

e.g.
Code:
#define FOSC  128000000
#define CLOCKS_PER_MACHINE_CYCLE  2
#define TIMER_DIV_VALUE 64
#define CLOCK_RATE_HZ  1000

#define CLOCK_VALUE_TICKS (65536 - (FOSC / (CLOCKS_PER_MACHINE_CYCLE * TIMER_DIV_VALUE * CLOCK_RATE_HZ)))

Then use CLOCK_VALUE_TICKS in your set_timer3() function. After all, the value isn't going to change so there's no point in computing it in your program. Using a #define forces the compiler to compute it and then it's just a constant in your program - nothing to compute.
sd_tom



Joined: 06 Feb 2018
Posts: 3

View user's profile Send private message

PostPosted: Tue Feb 06, 2018 11:30 am     Reply with quote

will try the constants

Got an oscope attached this morning to double check where I'm at:

Code:
    clock_TimerValue = 65536 - (128000000/2/8/clock_tickHz); // Fosc/2 / DIV / frequency
    setup_timer3(TMR_INTERNAL | TMR_DIV_BY_8);

=1000Hz / 1.002ms period = Good

Code:

clock_TimerValue = 65536 - (128000000/2/64/clock_tickHz); // Fosc/2 / DIV / frequency
setup_timer3(TMR_INTERNAL | TMR_DIV_BY_64);

= 10hz / 100ms period

Code:

    clock_TimerValue = 65536 - (128000000/2/256/clock_tickHz); // Fosc/2 / DIV / frequency
    setup_timer3(TMR_INTERNAL | TMR_DIV_BY_256);

= 3.85hz/ 260ms period
Ttelmah



Joined: 11 Mar 2010
Posts: 19498

View user's profile Send private message

PostPosted: Tue Feb 06, 2018 11:44 am     Reply with quote

Setting timers 'to' a value is always inaccurate. On the PIC24/30 etc., you don't ever have to do this. Let the timer reload itself.
Then remember that the timer is not fed off the CPU clock. It is fed off the peripheral clock. Normally Fcpu/2.
So:
128MHz/2 = 64MHz.
64000000/1000 = 64000.

So /64 with a 1000 counts is perfect:

setup_timer3(TMR_INTERNAL | TMR_DIV_BY_64,999);

The actual period is always the period register+1, so 999, gives 1000 counts.

Get rid of the code setting the timer 'to' values, and your interrupt will be at 1000Hz.

Code:

#int_timer3
void timer_tick(void)
{
    // 1 sec printout
    if (++clock_gticks==1000)
    {
        clock_gticks=0;
        output_toggle(PIN_H0);
    }
}

Then you have a big problem:

115200baud-> 11500 characters per second.

Your printf, prints 9 characters plus the count. Once the count grows past 2 digits, you run out of time to do this at 1000Hz.

If you 'must' print the value, shorten as much as possible. Just a letter C, no space, then four hex character only, and a single line feed.
sd_tom



Joined: 06 Feb 2018
Posts: 3

View user's profile Send private message

PostPosted: Tue Feb 06, 2018 12:47 pm     Reply with quote

Thanks the period works perfectly. I guess what I don't get is what's the difference (I know one is more accurate, but order of magnitudes seems off)

Code:

#define FOSC  128000000
#define CLOCKS_PER_MACHINE_CYCLE  2
#define TIMER_DIV_VALUE 64
#define CLOCK_RATE_HZ  1000

#define PERIOD (FOSC / CLOCKS_PER_MACHINE_CYCLE / TIMER_DIV_VALUE / CLOCK_RATE_HZ)


// Works Perfect - Thanks!
Code:

setup_timer3(TMR_INTERNAL | TMR_DIV_BY_64, PERIOD-1);


// two orders of magnitude slow
Code:

    setup_timer3(TMR_INTERNAL | TMR_DIV_BY_64);
    set_timer3( 65536 - PERIOD ); // and set again in ISR


So, dunno.. I'm happy to take the answer and run but there seems to be a lot of historical posts about the 65536 - X approach, is it something about the PIC24?
Ttelmah



Joined: 11 Mar 2010
Posts: 19498

View user's profile Send private message

PostPosted: Tue Feb 06, 2018 3:36 pm     Reply with quote

The 65536-X approach is the only way you can do it on the PIC16/18. It is always less good than using a genuine division. so you are better off either using the 'increment by cycles' approach (in the code library), or Timer2 on these PIC's (the only timer with a programmable period on most of these).
newguy



Joined: 24 Jun 2004
Posts: 1907

View user's profile Send private message

PostPosted: Tue Feb 06, 2018 4:38 pm     Reply with quote

sd_tom wrote:
Thanks the period works perfectly. I guess what I don't get is what's the difference (I know one is more accurate, but order of magnitudes seems off)

Code:

#define FOSC  128000000
#define CLOCKS_PER_MACHINE_CYCLE  2
#define TIMER_DIV_VALUE 64
#define CLOCK_RATE_HZ  1000

#define PERIOD (FOSC / CLOCKS_PER_MACHINE_CYCLE / TIMER_DIV_VALUE / CLOCK_RATE_HZ)


// Works Perfect - Thanks!


Just a tiny note of caution:
Code:
#define PERIOD (FOSC / CLOCKS_PER_MACHINE_CYCLE / TIMER_DIV_VALUE / CLOCK_RATE_HZ)


While not wrong, this makes me very nervous because it assumes that the divisions will be done in a certain order (left to right). It may never change, but explicitly grouping all the denominators together into one factor (using * and ()) forces the math to progress in one way only which assures that the result will always be what you expect.
Ttelmah



Joined: 11 Mar 2010
Posts: 19498

View user's profile Send private message

PostPosted: Wed Feb 07, 2018 8:14 am     Reply with quote

C, and it's preprocessor explicitly do evaluate left to right unless there is a precedence rule involved. It ought to be right, but I would prefer some form of sanity checking. Particularly for another reason: While in the example given, the result should always be <65536, if used for a less frequent interrupt and it gives a result that is larger than this the result could well be screwed...
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group