Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

TMR0-1 Second Delay Help

Status
Not open for further replies.

Suraj143

Active Member
I need to get a pulse for every one second.until now i got that with delay loops,listed below.now i have to take that pulse from TMR0 without delay loops.how to do that?how the code comes?what is the meaning of prescaler?
I'm using 4mhz cystal-PIC16F84A.

Code:
Delay     MOVLW 03h
          MOVWF 1Ah
Delay1    DECFSZ 1Bh,1
          GOTO Delay1
          DECFSZ 1Ch,1
          GOTO Delay1
          DECFSZ 1Ah,1
          GOTO Delay1
          RETURN

Code:
cblock
	  d1
	  d2
	  d3
	  endc

			;999997 cycles
	  movlw	0x08
	  movwf	d1
	  movlw	0x2F
	  movwf	d2
	  movlw	0x03
	  movwf	d3
Delay_0
	  decfsz	d1, f
	  goto	$+2
	  decfsz	d2, f
	  goto	$+2
	  decfsz	d3, f
	  goto	Delay_0

			;3 cycles
	  goto	$+1
	  nop
	  return
 
hi suraj,

To use TMR0 interrupt, you will have to enable TMR0 intr.
The clock rate into the TMR0, 8 bit counter is the crystal freq divided by 4.
example if the crystal is 4mHz then the clock rate into the TMR0 reg is 1mHz. [1uSec]

The prescaler is a 8 bit counter divider, which you set in combination with the TMR0 8 bit reg value.

By dividing the 1uSec [1mHz] clock by the prescaler value and using the prescaler output to clock the 8 bit TMR0 reg it will give you
a 'known' interrupt interval. The interrupt occurs as TMR0 reg 'fills and rolls back to zero'.

As the prescaler and TMR0 reg division interrupt interval cannot have an interval as long as 1 second, you have to count the the interrupts until the count value is equivalent to 1 second.

Chose a prescaler and TMR0 preset values that will give you an integer value equal to 1 second.
Dont forget to allow for time that you 'use' while you are servicing the ISR [ interrupt service routine]

Rough example: say your prescale and TMR0 reg combination, gave an intr every 10mSec then you would count 100 interrupts.

Read the 16F84 datasheet regarding TMR0 and ALL the interrupt requirements.


EricG
 
Last edited:
Hi erric thankx for your reply.I studied the data sheet & gathered some informations from it.I aplied it to a coding listed below.
Code:
ORG 	0X04		;INTERUPT VECTOR	

	 BTFSS	INTCON,TOIF	;TMR0 INTERUPT?
	 GOTO	INTEND		;IF NO RETURN
	 DECFSZ	COUNTERL,1	;DECREMENT 100 TIMES
	 GOTO	INTEND		;IF NO RETURN
	 GOTO	TURNONLED	;TURN ON LED

INTEND	 BCF	INTCON,TOIF	;CLEAR TOIF FLAG BIT
	 RETFIE			;RETURN FROM INTERUPT
		

	 ORG	0X00

MAIN	 BSF 	STATUS,RP0	 ;SWITCH TO BANK 1
	 CLRF 	TRISB		 ;MAKE ALL PORTB PINS OUTPUT
	 ?????????????
	 MOVWF 	OPTION
	 BCF 	STATUS,RP0	 ;SWITCH TO BANK 0

	 BSF	INTCON,GIE
	 BSF	INTCON,TOIE
	 
	 MOVLW 	D'100	    	;(100x??=1SECOND)
	 MOVWF 	COUNTERL

LOOP	 GOTO LOOP		;WAIT UNTIL INTERUPT OCCURS
ericgibbs said:
Chose a prescaler and TMR0 preset values that will give you an integer value equal to 1 second.
How to choose a prescaler & TMR0 preset values?

Rough example: say your prescale and TMR0 reg combination, gave an intr every 10mSec then you would count 100 interrupts.
So how can I generate 10ms interupt?If I do that I can decrement that until 100 times & can generate 1 second (10x100=1sec).You can see in my codings I have moved D'100 for counterL.

In my codings the values that must be moved to option reg kept blanked.b'cuz I dont know how to load that.In the data sheet below option reg I see a table like this,
Code:
TMR0 rate

1:2
1:4
1:8
1:16
1:32
1:64
1:128
1:256
What does it means?what value should I choose?
 
hi

As you are using a 4mHz crystal, the frequency input to the TMR0 prescaler 1mHz [1uSec]

Example1:
If you set the TMR0 prescaler to say 256, it would mean that the frequency input to the 8 bit TMR0 reg
would be 3906.25hZ [int 3906 == 256uSec]

If you now loaded the TMR0 reg with say 0x00, it would require 256 pulses of the 256uSec from the output from the prescaler to make it 'overflow', which will cause a TMR0 interrupt.

So 256 * 256uSec = 65.54 milliSec period for each intr.

Example2:
If you had set the prescaler to say, 128, the output period from the prescaler would at
128uSec, so 128uSEc * 256 [the TMRo reg] = 32.77milliSec.

Example3:
Keep the prescaler set to say 128 = 128uSec and load the TMR0 reg with say, 0x80

So to make the TMR0 reg 'overflow' would now only take 128 counts, 128uSec * 128 =16.38millisSec


Got the idea, choose values for the prescaler and TMR0 reg to give you the interrupt period you require.


I would choose values that gave me a intr every 50milliSec, then just count 20 interrupts for 1 Sec.

Don't forget to allocate the TMR0 to the prescaler, [not the WDT]
Enable the intr you are using and the global.
On intr in your ISR clear the T0IF flag and if you have loaded the TMR0 reg with a value, reload it.
Use retfie at the end of your ISR, to re-enable intr.

Regards
Eric
 
Last edited:
Hi erric thanks for giving knowledge.Now I understood a little more.I dont like to give you more trouble but I have to ask this .

for a 4mHz crystal, the frequency input to the TMR0 prescaler 1mHz [1uSec]
for a 20mHz crystal, the frequency input to the TMR0 prescaler = [0.2uSec]

If i choose 256 from the prescaler
256*256usec = 65.54 millisec will generate an interupt.

If i choose 128 from the prescaler
128*256usec = 32.77 millisec will generate an interupt.

If i choose 32 from the prescaler
32*256usec = 8.192 millisec will generate an interupt.

I'm confused with your example3
Keep the prescaler set to say 128 = 128uSec and load the TMR0 reg with say, 0x80

So to make the TMR0 reg 'overflow' would now only take 128 counts, 128uSec * 128 =16.38millisSec
It should be 128*256=32.77 millisec isn't it.why did you again multiply with 128 ?what happened to that 0x80?

Prescaler & the TMR0 reg are two different things.If loaded TMR0 as 0x00 then I must multiply the prescaler rate from 256.can I load any value (upto 255) to the TMR0 like this?
MOVLW 0X20 ;load 20 into TMR0
MOVWF TMR0
Is this right?then I can replace my COUNTERL Reg with this TMR0 Reg.then I can count up the TMR0 Reg until how many times it has loaded.

for every 50millisec *20 = exactly 1 second.so how can I choose 50 millisec it from the prescaler its all 8 dividers like 2,4,8,16,32,128,256.there is no 50.
 
TMR0 reg is incremented every time the prescaler overflows. An interrupt is generated every time that TMR0 reg goes from 255 to 0. Therefore, if you set the prescaler to 128 and load TMR0 reg with 256-78 then you will get an interrupt after 78*128 clock cycles. 78*128 = 9984 - almost 10mS. The other 16clock cycles required to make this exactly 10ms can be at the start of your interrupt service routine (ISR).

I thought I better test my theory and here is the result,
Code:
		#include <P16F84A.inc>
		errorlevel -302
		cblock	70h
int_work
int_status
		endc

		org	0x0000
		goto	Start

		org	0x0004
interrupt	movwf	int_work;	2latent	+1 =3
		swapf	STATUS,W;		+1 =4
		movwf	int_status;		+1 =5
		bcf	STATUS,RP0;		+1 =6
		bcf	STATUS,RP1;		+1 =7
		movlw	100h-d'78';		+1 =8
		goto	$+1;			+2 =10
		goto	$+1;			+2 =14
		nop;				+1 =15
		movwf	TMR0;			+1 =16 + 78*128 = 10,000
		bcf	INTCON,T0IF;		reset int flag
		swapf	int_status,W
		movwf	STATUS
		swapf	int_work,F;	swap to file
		swapf	int_work,W;	swap to work
		retfie

Start		bcf	STATUS,RP1
		bsf	STATUS,RP0
		bcf	STATUS,IRP
		movlw	b'00000110';		set prescaler to 128
		movwf	OPTION_REG
		bcf	STATUS,RP0
		movlw	0a0h;		enable timer interupt 
		movwf	INTCON
hang		goto	hang

		end

The above code when run in the simulator produces an interrupt every 10mS.

Mike.
 
hi suraj,

Did you follow ok what Mike said ?

Consider the prescaler as a programable divider of the osc/4 frequency.
It can be programmed to divide the osc/4 freq by, 2 to 256 in powers of 2.

The output of the prescaler divider is input the TMR0 reg [8 bit counter].

As the TMR0 counter 'fills' and rolls back to zero an interrupt is generated.

You can preload the TMR0 counter with a decimal value from 0 to thru 255.

Say you half 'fill' the TMR0 with 128, then it only requires 128 counts [from the prescaler output] to reach 255 counts and roll back to 000, and generate an interrupt.

As I and Mike have pointed out, for accurate intr periods, count the instructions in the ISR, allow a osc/4 'time period'
[ 1uSec in your example] for each instruction, then the sum of all the instruction periods is then to added to the 'prescaler and TMR0' times.

Note:
The label TMR0 is a dedicated/reserved register 'label', you can't use it as a interrupt counter register.
Use a free ram register, example, in ASM absolute coding terms.

ISR_CNT EQU 0x20

Regards
Eric

Thanks Mike.
 
Last edited:
Hi thanks for both of you giving me a great knowledge of TMR0.Now I understand it very well.
Yes Erric I'm ok with mikes codings. It was helped me a lot.

But I have a simple question to ask from mike.
*
78*128 = 9984 - almost 10mS.thats correct.

But why did you move like this for TMR0?

movlw 100h-d'78'

The result of this line will be 256-78 = 178 not d’78

Can I directly move d’78 like this

movlw d'78'

Thanks Mike, Thanks Erric
You are really experts
 
The reason for loading 100h-d’78’ is because TMR0 counts up. It is just a (weird) coincidence that 100h-78 = 178. If we had loaded TMR0 with 250 then the delay would have been (256-250)*128 – we load it instead with 178 which gives a delay of (256-178)*128 which happens to be 78*128. So, you can’t put movlw d’78’ but you can do movlw d’178’, but I think the way it is makes more sense.

Hope that didn’t confuse you.

Mike.
 
Hi I was working with the TMR0.I added some extra lines to Mikes coding to turn on the LED for every 1 second.here I loaded counterL as D'100 so 10msx100=1second.

I have added a 0.5delay in the main loop to see the LED flash.and the other 0.5 seconds will turn off the LED.

But I'm having a problem in the output I cannot see the LED flash.Why is that?I think I have put that new routine in the wrong place.

Code:
#include <P16F84A.inc>
		errorlevel -302
		cblock	70h
int_work
int_status
[B]counterL
d1
d2
d3[/B]
		endc

		org	0x0000
		goto	Start

		org	0x0004
interrupt	movwf	int_work	;2latent +1 =3
		swapf	STATUS,W	;+1 =4
		movwf	int_status	;+1 =5
		bcf	STATUS,RP0	;+1 =6
		bcf	STATUS,RP1	;+1 =7
		movlw	100h-d'78'	;+1 =8
		goto	$+1		;+2 =10
		goto	$+1		;+2 =14
		nop			;+1 =15
		movwf	TMR0		;+1 =16 + 78*128 = 10,000

		[B]btfss	INTCON,T0IF	;Is it a TMR0 interupt?
		goto	intend		;return from interupt
		decfsz	counterL,1
		goto	intend		;return from interupt
		bsf	PORTB,0		;turn on the LED RB0
		movlw	64h		;load 100 to counterL
		movwf	counterL[/B]

intend		bcf	INTCON,T0IF	;reset int flag
		swapf	int_status,W
		movwf	STATUS
		swapf	int_work,F	;swap to file
		swapf	int_work,W	;swap to work
		retfie

Start		bcf	STATUS,RP1
		bsf	STATUS,RP0
		bcf	STATUS,IRP
		clrf	TRISB
		movlw	b'00000110'	;set prescaler to 128
		movwf	OPTION_REG
		bcf	STATUS,RP0
		movlw	0a0h		;enable timer interupt 
		movwf	INTCON
		[B]movlw	64h		;load 100 to counterL
		movwf	counterL

                call	del		;call a 0.5 seconds delay to see the LED flash
		bcf	PORTB,0		;other 0.5 seconds LED will off[/B]
Loop		goto	Loop		;Loop 

[B]del		movlw	0x03		;0.5 seconds delay routine
		movwf	d1
		movlw	0x18
		movwf	d2
		movlw	0x02
		movwf	d3
Delay_0
		decfsz	d1, f
		goto	$+2
		decfsz	d2, f
		goto	$+2
		decfsz	d3, f
		goto	Delay_0

		goto	$+1
		goto	$+1
		goto	$+1
		return
[/B]

		end
 
The reason your LED isn't flashing is because the IRQ won't turn on the LED until 1 second has passed and your delay is 0.5 seconds.

A better way is to test counterL to see which half of the second you are in. You do this by comparing it to 50 and setting the LED dependent on the result. Something like,
Code:
Start		bcf	STATUS,RP1
		bsf	STATUS,RP0
		bcf	STATUS,IRP
		clrf	TRISB
		movlw	b'00000110'	;set prescaler to 128
		movwf	OPTION_REG
		bcf	STATUS,RP0
		movlw	0a0h		;enable timer interupt 
		movwf	INTCON
		movlw	64h		;load 100 to counterL
		movwf	counterL

Loop		movfw	counterL	;get the count
		sublw	d'50'		;sub 50 from it
		btfsc	STATUS,C	;if carry set
		bsf	PORTB,0		;turn on LED
		btfss	STATUS,C	;if carry clear
		bcf	PORTB,0		;turn off LED
		goto	Loop		;Loop

Mike.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top