|
|
View previous topic :: View next topic |
Author |
Message |
SpoonBilly
Joined: 25 May 2010 Posts: 16
|
dsPIC33F Timer Interrupt do not work for Prescaler 64 & |
Posted: Tue Oct 05, 2010 9:29 pm |
|
|
I am just started on dsPIC33F, work on PIC18F mostly in the past.
I was intended to run the dsPIC33FJ128GP306 at 40MIPS, that is 80MHz. It works fine for timer prescaler 1 and 8, but cant get the timer interrupt working for prescaler 64 and 256.
My code:
Code: |
#include <33FJ128GP306.h>
#device ICD = TRUE
#fuses XT, NOWDT, PR_PLL, NOWDT, NOCOE, NODEBUG, NOWRTB, NOPUT, NOWRTSS, NOWRT, NOPROTECT, NORSS
#use delay (clock = 80M, crystal = 10M) // 10MHz Oscillator
#int_TIMER1
void TIMER1_isr()
{
set_timer1(53036) ;
output_d (0b00000001) ; // Illuminate LED at RD0 (0b00000001 = 0x01)
delay_us(500) ; // Wait for 500us
output_d (0b00000000) ; // Illuminate LED at RD0 (0b00000000 = 0x00)
}
void main()
{
setup_timer1(TMR_INTERNAL|TMR_DIV_BY_64) ; // Start timer 1, prescaler = 64
enable_interrupts(INT_TIMER1) ;
enable_interrupts(INTR_GLOBAL) ;
while(TRUE) ;
}
|
f_d = desired frequency
f_c = clock frequency
p = prescaler
t_s = value of which the timer needs to be set
t_s = 65536 - (1/f_d)*f_c/p/2
*dsPIC33F has 2 clock cycles per execution cycle, instead of 4.
In this case,
f_d = 50 Hz
f_c = 80000000
p = 64
Thus, t_s = 53036
Any idea? Thanks. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Wed Oct 06, 2010 12:53 am |
|
|
Why do you think, the timer interrupt doesn't work?
If you can afford a 500 us delay in the ISR, why don't you stay with slow PIC18. |
|
|
SpoonBilly
Joined: 25 May 2010 Posts: 16
|
|
Posted: Wed Oct 06, 2010 1:00 am |
|
|
FvM wrote: | Why do you think, the timer interrupt doesn't work?
If you can afford a 500 us delay in the ISR, why don't you stay with slow PIC18. |
I am new to dsPIC. Currently in the process of trying things out. The output high and low performed by the interrupt is to allow me justify the interrupt execution by using oscilloscope. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Wed Oct 06, 2010 6:03 am |
|
|
You are using a wrong method to setup timer1, I think.
You should set the timer period during initialization and not rewrite the timer in the ISR.
Code: | setup_timer1(TMR_INTERNAL|TMR_DIV_BY_64,12500); |
I also noticed, that you are apparently using an old PCD version. If you still have problems with toimer operation, please tell your PCD version. |
|
|
SpoonBilly
Joined: 25 May 2010 Posts: 16
|
|
Posted: Wed Oct 06, 2010 6:11 am |
|
|
FvM wrote: | You are using a wrong method to setup timer1, I think.
.... |
Thanks FvM.
The way I set the timer, it works fine for prescaler 1 and 8. Thanks for your advice. It is night over here, I will give it a try tomorrow and let you know.
.
.
.
.
.
I'm using PCD 4.093. Just tried it out. It works!!! Thank you very very much.
Last edited by SpoonBilly on Wed Oct 06, 2010 8:17 pm; edited 1 time in total |
|
|
SpoonBilly
Joined: 25 May 2010 Posts: 16
|
|
Posted: Wed Oct 06, 2010 8:08 pm |
|
|
Even if the advice given by FvM works perfectly, but being able to set_timer inside the timer interrupt ISR is crucial if the interrupt execution rate is not always constant.
After some trials and errors, I came out with this:
Code: |
#include <33FJ128GP306.h>
#device ICD = TRUE
#fuses XT, NOWDT, PR_PLL, NOWDT, NOCOE, NODEBUG, NOWRTB, NOPUT, NOWRTSS, NOWRT, NOPROTECT, NORSS
#use delay (clock = 80M, crystal = 10M) // 10MHz Oscillator
#int_TIMER1
void TIMER1_isr()
{
//set_timer1(62411) ; // for some reasons, this doesn't work for prescaler 64 and 256
// Timer1 is a 16 bit timer which means it overflows every 65536 counts
// The Timer1 is triggled every time it overflows.
// Let, f_d = desired frequency; f_c = clock frequency; p = prescaler
// t_s = value of which the timer needs to be set
// t_s = 65536 - (1/f_d)*f_c/p/2
//
// In this case, f_d = 50; f_c = 80000000; p = 256
// Thus, t_s = 62411
/*
for some unknown reasons, for prescaler 64 and 256, delay has to be introduced before set_timer
delay_cycles(39) ; // delay >= 39 instruction clocks. works for prescaler 64
delay_cycles(231) ; // delay >= 231 instruction clocks. works for prescaler 256
// for dsPIC33F, 2 oscillator clock = 1 instruction clock.
// for clock = 80MHz, 39 instruction clocks = 1/80M*2*39 = 0.975us
// for clock = 80MHz, 231 instruction clocks = 1/80M*2*231 = 5.775us
set_timer1(62411) ; // introducing delay comes with a price, the timer will no longer be accurate.
Thus,
for clock = 80MHz and prescaler 64, instead of delay_cycles(39), one can use delay_cycles(64)
As such:
// 64 instruction clocks = 1/80M*2*64 = 1.6us = 1 timer tick
delay_cycles(64) ;
set_timer1(t_s+1) ;
for clock = 80MHz and prescaler 256, instead of delay_cycles(231), one can use twice delay_cycles(128)
As such:
// 256 instruction clocks = 1/80M*2*256 = 6.4us = 1 timer tick
delay_cycles(128) ;
delay_cycles(128) ;
set_timer1(t_s+1) ;
Alternatively,
if the timer interrupt is to be executed at a constant rate,
instead of set_timer inside the timer interrupt, one can setup_timer in the main function.
for example:
setup_timer1(TMR_INTERNAL|TMR_DIV_BY_256,3125) ; // Start timer1, prescaler = 256, interrupt executed for every 3125 timer ticks
*/
delay_cycles(128) ;
delay_cycles(128) ;
set_timer1(62412) ; // 62411 + 1
output_d (0b00000001) ; // Illuminate LED at RD0 (0b00000001 = 0x01)
delay_us(500) ; // Wait for 500us
output_d (0b00000000) ; // Illuminate LED at RD0 (0b00000000 = 0x00)
}
void main()
{
setup_oscillator(OSC_CRYSTAL) ;
setup_timer1(TMR_INTERNAL|TMR_DIV_BY_256) ; // Start timer1, prescaler = 256
//setup_timer1(TMR_INTERNAL|TMR_DIV_BY_256,3125) ; // Start timer1, prescaler = 256, interrupt executed for every 3125 timer ticks
enable_interrupts(INT_TIMER1) ;
enable_interrupts(INTR_GLOBAL) ;
while(TRUE) ;
}
|
Any comment is welcomed |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Wed Oct 06, 2010 9:48 pm |
|
|
I also noticed, that rewriting the timer register doesn't work always as intended. I didn't check the documentation, but I guess, that the timer is reset to zero at the next divided clock after the interrupt, so if you manage to rewrite the timer before, the rewrite is possibly ignored. The behaviour is also reproduced by the MPLAB simulator, so I think it's basically well defined.
But, you didn't tell a motivation for rewriting the timer register. In most cases, even if a variable timer intervall is intended, one would write the preset register, because it's the only way to achieve a timer intervall that's exact to a clock tick. |
|
|
SpoonBilly
Joined: 25 May 2010 Posts: 16
|
|
Posted: Wed Oct 06, 2010 10:09 pm |
|
|
FvM wrote: | ...In most cases, even if a variable timer intervall is intended, one would write the preset register, because it's the only way to achieve a timer intervall that's exact to a clock tick. |
I learnt the way I work with the timer interrupt from a book ("Embedded C Programming and the Microchip PIC" by Richard Barnett, Larry O'Cull, & Sarah Cox, ISBN: 1401837484, pg. 110).
Didn't quite get what you mean. Is there any example code? |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Oct 07, 2010 10:39 am |
|
|
To understand the differences between writing PR1 and TMR1 register, you should consult the processor hardware manual. If you want an exact timer period, you have to use PR1. The timer generates an interrupt when the timer register equals PR1 and resets the timer register automatically. So the timer interval is independant of accidental variations. If you rewrite TMR1 (e.g. with set_timer() ), the actual timer period is increased by the interrupt latency and processing time until set_timer is executed. As far as I see, CCS C doesn't provide a function to write PR1, except setup_timer(), but you can always define the register yourself and assign a new value. |
|
|
SpoonBilly
Joined: 25 May 2010 Posts: 16
|
|
Posted: Fri Oct 08, 2010 1:02 am |
|
|
FvM wrote: | ... If you rewrite TMR1 (e.g. with set_timer() ), the actual timer period is increased by the interrupt latency and processing time until set_timer is executed... |
hm... well, anyway, I have been using the timer interrupt on PIC18F for quite some time. It is not very accurate, but it serves my purpose. After all, I am not going to build an accurate clock.
FvM wrote: | ...CCS C doesn't provide a function to write PR1, except setup_timer(), but you can always define the register yourself and assign a new value. |
You are right about writing PR1, though in order to variate timer interrupt execution rate, I have no other idea but to write PR1 inside the timer interrupt ISR... this doesn't make it accurate either, I suppose~ But it works for prescaler 64 and 256, better than introducing a delay before set_timer as I suggested in the previous post.
Just to share the code of how to write PR1 with the rest of the CCS community:
Code: |
#include <33FJ128GP306.h>
#device ICD = TRUE
#fuses XT, NOWDT, PR_PLL, NOWDT, NOCOE, NODEBUG, NOWRTB, NOPUT, NOWRTSS, NOWRT, NOPROTECT, NORSS
#use delay (clock = 80M, crystal = 10M) // 10MHz Oscillator
#word PR1 = 0X0102 // Period Register 1
#int_TIMER1
void TIMER1_isr()
{
PR1 = 3125 ; // changes PR1 register
output_d (0b00000001) ; // Illuminate LED at RD0 (0b00000001 = 0x01)
delay_us(500) ; // Wait for 500us
output_d (0b00000000) ; // Illuminate LED at RD0 (0b00000000 = 0x00)
}
void main()
{
setup_oscillator(OSC_CRYSTAL) ;
setup_timer1(TMR_INTERNAL|TMR_DIV_BY_256) ; // Start timer1, prescaler = 256
enable_interrupts(INT_TIMER1) ;
enable_interrupts(INTR_GLOBAL) ;
while(TRUE) ;
}
|
|
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Fri Oct 08, 2010 2:41 am |
|
|
Your code sets PR1 to a constant value, this must be done only once. I don't see a reason, why this should be done inside the ISR, it's sufficient to set it with the initial setup_timer1(), as in my above example.
Varying PR1 during program execution would be a different thing, but you didn't mention this requirement yet. |
|
|
SpoonBilly
Joined: 25 May 2010 Posts: 16
|
|
Posted: Fri Oct 08, 2010 7:18 am |
|
|
FvM wrote: | Your code sets PR1 to a constant value, this must be done only once. I don't see a reason, why this should be done inside the ISR, it's sufficient to set it with the initial setup_timer1(), as in my above example.... |
Yup~ the code is just to demonstrate that it is possible to do so. Indeed, if a constant value is desired, your example code is sufficient.
I haven't study the effect of writing PR1 in depth, I didn't know that varying PR1 during program execution would bring upon any side effect. |
|
|
|
|
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
|