Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

TMR0 issue, can't achieve precise 1 sec count

Status
Not open for further replies.

T3STY

Full Member level 4
Full Member level 4
Joined
Apr 17, 2012
Messages
239
Helped
24
Reputation
48
Reaction score
24
Trophy points
1,308
Visit site
Activity points
3,715
I'm working on making the 16F84A work with an LCD display, as you may know already. Meanwhile I'm using the LED test circuit to test some timings.
I'm trying now to achieve a 1 second counter using TMR0, which, supposedly, should be far more precise than a SW delay routine, with no success. This is the code I am using:
Code:
#define _XTAL_FREQ 4000000
#include <htc.h>

/* XTAL Oscillator
 * Watchdog timer OFF
 * Power up timer OFF
 * Code protection OFF
 */
__CONFIG(FOSC_XT & WDTE_OFF & PWRTE_OFF & CP_OFF);

int TMR0_CK=0;	// here I'll count how many TMR0 CKs have been generated
char cnt=0;		// Here I count, every 32 TMR0 CKs, how many seconds have passed

void main(void){
	// Intializing ports as outputs, and port value 0
	TRISA=0x00;
	TRISB=0x00;
	PORTA=0x00;
	PORTB=0x00;
	
	// Interrupts {GIE=1, T0IE=1}
	INTCON=0xA1; 	 // | GIE=1 | EEIE | T0IE=1 | INTE | RBIE | T0IF | INTF | RBIF |
	// Prescaler assignment: TMR0
	// Prescaler value: (bin) 100 -> 1/32
	// Human translated: 1 TMR0 CK is generated every 31250 main XTAL clocks ( (4MHz/4)/32 )
	OPTION_REG=0x84; // | RBPU | INTEDG | T0CS | T0SE | PSA=0 | PS2=1 | PS1=0 | PS0=0 |
	
	// The code runs from the interrupt routine from now on,
	// it will count clocks and update PORTB value
	// the counters and other intructions will take up a few cycles
	// but they're actually very few, not really important
}

void interrupt T0ISR(){
	if (T0IF){ // TMR0 CK has been generated (TMR0 overflow)
		TMR0_CK++;	// Update TMR0 CK counter
		
		if (TMR0_CK == 32){
			// We reached 1s
			// 32*31250 = 1MHz = 1s
			
			// Resetting TMR0 CK counter
			TMR0_CK=0;
			
			// Updating the output
			PORTB = cnt++;
		}
		
		// Releasing interrupt
		T0IF=0;
	}
}
What it does though, is counting... too much? I'm not sure what is it counting at all, but I get the first "second" after a few minutes, and that's on the real PIC. Simulation fails too pretty much the same way, at longer time. The reasoning behind all of this code was:
I have 4MHz crystal, which means that every macro instruction will be executed at the real speed of 1MHz. 1MHz means that 1 second equals 1,000,000 (marco)instructions. I have TMR0 that could not use the same speed to be triggered, it must use at least a prescaler of 1:2. The highest prescaler value that gives me an integer result is 1:32, which triggers TMR0 overflow every 31250 CKs of the main CK. If so, I should count 32 times the 31250 CK to get back 1MHz. So if count this in a variable, at counter value 32 I should have reached 1s with a very low approximation due to some non-counting instructions.

What is wrong then? Why am I not getting the 1 second counter?
 

Use this code. It is for PIC16F, Timer0, 4 MHz. Use a counter to count 100 as this timer code is for 10 ms interrupt. So, 10 ms * 100 = 1000 ms = 1 sec.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Timer0
//Prescaler 1:64; TMR0 Preload = 97; Actual Interrupt Time : 9.986 ms
 
//Place/Copy this part in declaration section
void InitTimer0(){
  OPTION_REG     = 0x85;
  TMR0       = 97;
  INTCON     = 0xA0;
}
 
void Interrupt(){
  if (TMR0IF_bit){ 
    TMR0IF_bit   = 0;
    TMR0         = 97;
    //Enter your code here
  }
}

 

You need a loop at the end of your program

while(1){
;
}

The code is running to the end, then reseting and restarting.
 

jayanth.devarayanadurga correctly points out two errors:
1. The time between interrupts is how long it takes TMR0 to reach full count (255) plus one clock to make it roll over to zero. Any time you spend in the interrupt routine before reloading the timer is added to the total time it takes. So your priority in the code should be to clear the interrupt then reload it as quickly as possible. The time taken clearing the interrupt and any other code you add before relaoding again makes the interval longer.
2. When you reset the PIC, the timer will clear to zero so it has to take a full 255 counts before the first interrupt occurs, this is why the first count takes longer than expected. You fix this by pre-loading the timer as you initialize the PIC so it has a good starting value.

Remember the counter counts up, not down so when you load it with a value use (255 - counts) so it rolls over in 'counts' intervals. In the code jayanth.devarayanadurga posted the interrupts will be every 10mS so where it says "Enter your code here" you need to count up to 100 to create a one second period. Hint: In most compilers it's more efficient to count down to zero than up to a value because the PIC has a single word instruction for testing for zero.

Generating delays is much easier with the later PIC devices, the 16F84A is quite old now. The newer types have 16 bit timers so counting by much bigger numbers is possible.

Brian.
 

I think I found what was failing my calculations. I didn't consider that before I get a TMR0 overflow, it must count to 255+1. If I consider it and divide again the TMR0_CK by 256 I get the real TMR0 overflow CK, which is what I need to count.
I could now get an almost precise 1s counter, it fails because it has an error of 1 second per minute, so I have 61 counted seconds per minute. I can't really avoid that but considering that I'll have to use this with other LCD functions and probably other manipulations, I guess I'll make it slow down to near 60 counted seconds.

Thanks to anyone for help!
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top