PDA

View Full Version : inaccurate frequency using TMR1 PI18F452



nkarpovich
- 12th October 2006, 00:22
I'm working with a PIC18F452 with a 20 MHz extrenal clock, writing a program with PBP 2.46
I'm generating pulses using PORTC.5 in the interrupt routine to drive a stepper motor.
I'm measuring the output pulse on pin 28 (portc.5) with an oscilloscope.
Variable PRESCALER is used to load TMR1

if PRESCALER = 65430 output frequency is 19.85 KHz
if PRESCALER = 65431 output frequency is 19.85 KHz
if PRESCALER = 65432 output frequency is 19.85 KHz
if PRESCALER = 65433 output frequency is 20.83 KHz !!!!!!!!!!!!!!!!!!!!!!!!!!
if PRESCALER = 65434 output frequency is 20.77 KHz
if PRESCALER = 65435 output frequency is 20.77 KHz
if PRESCALER = 65436 output frequency is 20.83 KHz
if PRESCALER = 65437 output frequency is 20.83 KHz
if PRESCALER = 65438 output frequency is 21.74 KHz !!!!!!!!!!!!!!!!
if PRESCALER = 65439 output frequency is 21.74 KHz
if PRESCALER = 65440 output frequency is 21.82 KHz

Can someone please explain me why I get 1 KHz frequency gaps ? Is this normal?

Below part of my code:

PRESCALER VAR WORD
PRESCALERL VAR PRESCALER.byte0
PRESCALERH VAR PRESCALER.byte1
T1CON = %00010100 'TIMER1 PRESCALER 1:2, TIMER , CLOCK INTERNO (F/4), STOP TIMER1 &&&

miint:
IF PIR1.0 = 1 THEN
T1CON.0 = 0
PORTC.5 = 0 'STEP pulse
TMR1H = PRESCALERH
TMR1L = PRESCALERL
PORTC.5 = 1 'PULSO DE STEP
ENDIF
T1CON.0 = 1
PIR1.0 = 0
Resume ' retorna al programa principal


Norberto Karpovich

mister_e
- 12th October 2006, 04:39
is that your whole code?

If so, there's few things missing about the interrupt declaration and handling (Disable, Enable, On interupt goto,...,.. )

i can't explain why it should longer to load one WORD value or Another. so it's certainely something elsewhere in the whole program.

Darrel Taylor
- 12th October 2006, 07:59
First off, You can't expect ON INTERRUPT to perform accurately in the range of 20khz. It's more of a, "Whenever I get around to it" interrupt system. If you want an accurate frequency in this range, you need to use ASM.

Second, you need to ADD the constant to the existing timer value, instead of just loading the number on each interrupt. Otherwise you lose however many "Ticks" the ON INTERRUPT waited before finally servicing it.

Third, you have to account for the time it takes to ADD the constant to the timer value. And for that, you need to know how long that takes. Hard to know with Basic Interrupts.

Forth, when everything is working correctly, a Const of 65430 will give an interrupt frequency of 22.8 khz instead of 19.8.

and Fifth, You're loading a value into the TIMER registers, not the prescaler.

Ok, let's go for sixth. Setting the T1SYNC (T1CON.2) bit has no effect when using the system clock (FOSC/4) for the timer input. Doesn't hurt, but doesn't do anything constructive either.

And, yes I've been having a bad day. Sorry! :o
<br>

mister_e
- 12th October 2006, 08:12
There's some days like that dude! Hopefully there's no male PMS... as far as i'm aware of :D


Forth, when everything is working correctly, a Const of 65430 will give an interrupt frequency of 22.8 khz instead of 19.8.


So i guess a tool comes handy now?
<img src="http://www.picbasic.co.uk/forum/attachment.php?attachmentid=1123&stc=1&d=1160636996">

Darrel Taylor
- 12th October 2006, 08:21
Hi Steve,

Yup, that's exactly what I used to figure it out. Extremely handy!

When you gonna release it?
<br>
P.S. Looks a little different again :)

mister_e
- 12th October 2006, 08:30
Yup it's getting better and better with the feedback i got from the BetaTester team :D I'm still ear open!

The final release is still unknown as i have few new 'lazy code generation tool' in my head :D

nkarpovich
- 13th October 2006, 19:32
Thanks Darrel for your reply,
1. I'm only showing part of the code, not all of it.
2. I'm not concerned about obtaining a frequency lower than it should be, I'm rather concerned about the gaps in frequency when changing by 1 the value I load into TMR1...
3. While the program is waiting for the interrupt it is only executing a while ...wend loop checking for two digital inputs, that's all. I presume the interrupt doesn't take that much in this case (no PAUSE instructions nor LCDOUT...)
4. When I add the ticks until interrupt routine executes the output frequency seems to be more unstable!!!!

suggestions are welcome, thanks a lot.


First off, You can't expect ON INTERRUPT to perform accurately in the range of 20khz. It's more of a, "Whenever I get around to it" interrupt system. If you want an accurate frequency in this range, you need to use ASM.

Second, you need to ADD the constant to the existing timer value, instead of just loading the number on each interrupt. Otherwise you lose however many "Ticks" the ON INTERRUPT waited before finally servicing it.

Third, you have to account for the time it takes to ADD the constant to the timer value. And for that, you need to know how long that takes. Hard to know with Basic Interrupts.

Forth, when everything is working correctly, a Const of 65430 will give an interrupt frequency of 22.8 khz instead of 19.8.

and Fifth, You're loading a value into the TIMER registers, not the prescaler.

Ok, let's go for sixth. Setting the T1SYNC (T1CON.2) bit has no effect when using the system clock (FOSC/4) for the timer input. Doesn't hurt, but doesn't do anything constructive either.

And, yes I've been having a bad day. Sorry! :o
<br>

nkarpovich
- 13th October 2006, 19:35
is that your whole code?

If so, there's few things missing about the interrupt declaration and handling (Disable, Enable, On interupt goto,...,.. )

i can't explain why it should longer to load one WORD value or Another. so it's certainely something elsewhere in the whole program.

Thanks Mister_e for your reply,
1. I'm only showing part of the code, not all of it.
2. I'm not concerned about obtaining a frequency lower than it should be, I'm rather concerned about the gaps in frequency when changing by 1 the value I load into TMR1...
3. While the program is waiting for the interrupt it is only executing a while ...wend loop checking for two digital inputs, that's all. I presume the interrupt doesn't take that much in this case (no PAUSE instructions nor LCDOUT...)
4. When I add the ticks until interrupt routine executes the output frequency seems to be more unstable!!!!

suggestions are welcome, thanks a lot.

nkarpovich
- 13th October 2006, 19:37
1. I'm only showing part of the code, not all of it.
2. I'm not concerned about obtaining a frequency lower than it should be, I'm rather concerned about the gaps in frequency when changing by 1 the value I load into TMR1...
3. While the program is waiting for the interrupt it is only executing a while ...wend loop checking for two digital inputs, that's all. I presume the interrupt doesn't take that much in this case (no PAUSE instructions nor LCDOUT...)
4. When I add the ticks until interrupt routine executes the output frequency seems to be more unstable!!!!

suggestions are welcome, thanks a lot.

Darrel Taylor
- 13th October 2006, 19:45
>> 4. When I add the ticks until interrupt routine executes the output frequency seems to be more unstable!!!!

That shouldn't be the case. Can you show the code for how you're adding it into the timer value?
<br>

nkarpovich
- 13th October 2006, 22:09
Disable
miint:
IF PIR1.0 = 1 THEN
T1CON.0 = 0
OFFCNT_H =TMR1H
OFFCNT_L =TMR1L
IF ARRANCA_MOTOR = 1 THEN
PORTC.5 = 0 'PULSO DE STEP
PORTA.5 = 0 'SALIDA PARA SHAFT
PRESCALER = FRECUENCIA + OFFCNT
TMR1H = PRESCALERH
TMR1L = PRESCALERL
PORTA.5 = 1 'SALIDA PARA SHAFT
PORTC.5 = 1 'PULSO DE STEP
ELSE
IF EMULACION = 1 THEN
IF ACTIVA_PRESENCIA = 0 THEN
TRIGGER = 0
ELSE
TRIGGER = 1
ENDIF
ENDIF
ENDIF
'************************************************* ************************************************** **
ENDIF
T1CON.0 = 1
PIR1.0 = 0
'**************************** INTERRUPCION POR TIMER 0 ****************************

salida1:
Resume ' retorna al programa principal
Enable

Darrel Taylor
- 13th October 2006, 22:41
You always want the timer to reload the same on each interrupt. By placing it inside IF/THEN statements, it can change depending on the conditions.

It also needs to be reloaded and re-started as soon as possible. Putting lot's of statements in-between turning the timer off and then back on again put's a variable amount of time in there as well.

Try it this way...
@Timer1 = TMR1L
Timer1 VAR WORD EXT

Disable
miint:
IF PIR1.0 = 1 THEN
T1CON.0 = 0
Timer1 = Timer1 + FRECUENCIA
T1CON.0 = 1

IF ARRANCA_MOTOR = 1 THEN
PORTC.5 = 0 'PULSO DE STEP
PORTA.5 = 0 'SALIDA PARA SHAFT
PORTA.5 = 1 'SALIDA PARA SHAFT
PORTC.5 = 1 'PULSO DE STEP
ELSE
IF EMULACION = 1 THEN
IF ACTIVA_PRESENCIA = 0 THEN
TRIGGER = 0
ELSE
TRIGGER = 1
ENDIF
ENDIF
ENDIF
'************************************************* ************************************************** **
PIR1.0 = 0
ENDIF
'**************************** INTERRUPCION POR TIMER 0 ****************************

salida1:
Resume ' retorna al programa principal
Enable
--------------------------------------------------------------------------------

sayzer
- 14th October 2006, 03:45
Let me edit a small part.



@Timer1 = TMR1L
Timer1 VAR WORD EXT

Disable
miint:
IF PIR1.0 = 1 THEN
T1CON.0 = 0
Timer1 = Timer1 + FRECUENCIA
T1CON.0 = 1

IF ARRANCA_MOTOR = 1 THEN
PORTC.5 = 0 'PULSO DE STEP
PORTA.5 = 0 'SALIDA PARA SHAFT
PORTA.5 = 1 'SALIDA PARA SHAFT
PORTC.5 = 1 'PULSO DE STEP
ELSE
IF EMULACION = 1 THEN TRIGGER = ACTIVA_PRESENCIA

ENDIF
'************************************************* ********
PIR1.0 = 0
ENDIF
'**************** INTERRUPCION POR TIMER 0 **************

salida1:
Resume ' retorna al programa principal
Enable
--------------------------------------------------------------------------------

nkarpovich
- 14th October 2006, 12:51
I've introduced the changes you suggested.
I'm VERY pleased to find that frequency is no longer a function of the interrupt routine duration!
Frequency output is still unstable and is constantly fluctuating end to end about 150 Hz when I set TMR1 to 65200 (7.3 KHz range). How can I improve frequency stability?
Thanks a lot.

mister_e
- 14th October 2006, 16:22
Use an assembler ISR or use 'Instant interrupts' by Mr Darrel Taylor. REALLY NICE STUFF!
http://www.pbpgroup.com/modules/wfsection/article.php?articleid=19