View previous topic :: View next topic |
Author |
Message |
sd_tom
Joined: 06 Feb 2018 Posts: 3
|
PIC24 - Sampling/clock timer |
Posted: Tue Feb 06, 2018 10:38 am |
|
|
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: 9246 Location: Greensville,Ontario
|
|
Posted: Tue Feb 06, 2018 10:46 am |
|
|
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: 1909
|
|
Posted: Tue Feb 06, 2018 10:53 am |
|
|
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
|
|
Posted: Tue Feb 06, 2018 11:30 am |
|
|
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: 19552
|
|
Posted: Tue Feb 06, 2018 11:44 am |
|
|
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
|
|
Posted: Tue Feb 06, 2018 12:47 pm |
|
|
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: 19552
|
|
Posted: Tue Feb 06, 2018 3:36 pm |
|
|
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: 1909
|
|
Posted: Tue Feb 06, 2018 4:38 pm |
|
|
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: 19552
|
|
Posted: Wed Feb 07, 2018 8:14 am |
|
|
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... |
|
|
|