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.

16F877 Timer0 Interrupt Affecting ADC

Status
Not open for further replies.

sebana

New Member
Hi guys, firstly, great forum. I can't find a more active microcontroller forum on Google!

I've been toying around with the 16F877 and I've had some problems, pertaining to Timer0 interrupt and ADC.

The problem: Enabling timer0 interrupt, affects my ADC values. Specifically when timer0 interrupt is disabled, my ADC values range properly from 0 -> 1024. But with timer0 interrupt enabled, ADC values range from 512 -> 1024. Weird problem! Do allow me to share the code:

Code:
void timer0_enable (void) {
    /* Initialise Timer 0 */
    OPTION = 0b11010111;  /* Set TMR0 to Internal CLk, 1:256 */
    T0IF = 0;             /* Clear TMR0 Flag, ready for use */
    T0IE = 1;             /* Enable Timer Overflow Interrupt */
}

void initialize_IO_ports (void) {
	//set the digital IO ports as per requirement
	TRISA = 0xFF ; //portA as input
	TRISB = 0B10000000 ; //portB as input
	TRISC = 0B11110000; //portC as output
	TRISD = 0B10000000; //portD as output
	ADCON1 = 0x82; //set PortE as digital io and portA as analog, and VDD and VSS; ADC output is right-justified.
	//clear the output ports at the beginning
	//PORTD = 0x00 ; //clear portD
	// PORTC = 0x00 ; //clear portC
	 
	GIE=1; //global interrupt enabled
 	PEIE=1; //peripheral interrupt enabled
}

void initialize_ADC (void) {
 	ADON=1; //enable the ADC
 	ADCS1=0; //set clock source for the ADC
 	ADCS0=0;
}

int read_ADC_channel (unsigned int channel_number) {
	int value;
 	switch (channel_number) {
		case 0: 
			CHS2=0; 
			CHS1=0; 
			CHS0=0;
 		case 1: 
			CHS2=0; 
			CHS1=0; 
			CHS0=1;
		case 2: 
			CHS2=0; 
			CHS1=1; 
			CHS0=0;
		case 3: 
			CHS2=0; 
			CHS1=1; 
			CHS0=1;
		default:;
 	}
 	ADGO=1; //start AD conversion
 	while(ADGO) {}; //wait for conversion to finish
 	value=(ADRESH<<8)+ADRESL; //read the values in the registers
 	return value;
}

void interrupt isr0 (void) {
	// for timer0
	if (T0IF == 1) { 
		luggage_present = luggage_ispresent();
		T0IF = 0;	// timer0 reload 
	}
}

void main() {

	int myInt;

	timer0_enable();	
	initialize_IO_ports();
	lcd_init();
	initialize_ADC();

// below is just a debugging tool to print ADC values


I'm thinking it's some problem with the way OPTION_REG is reacting with my ADC clock or something, but I've totally no idea after 2 days spent meddling and Googling. It's seriously killing me!

Any help is appreciated guys! Thanks!
 
sebana said:
I've been toying around with the 16F877 and I've had some problems, pertaining to Timer0 interrupt and ADC.

I would suggest using TMR2 instead. I've never had problems with it.
 
What if TMR2 is taking care something else?

I'm curious as to the cause of this problem as I am currently using TMR0, TMR2 and ADC in one of my projects... and would like to know the cause and solution if I end up running into this.

"Use something else" is the easy way, but it doesn't fix anything. It's kinda like saying "My car won't shift into second" and the solution being "don't worry about it... shift from first to third". ;)
 
Kyle-s4h said:
What if TMR2 is taking care something else?

I'm curious as to the cause of this problem as I am currently using TMR0, TMR2 and ADC in one of my projects... and would like to know the cause and solution if I end up running into this.
Hi Kyle, the code shows that TMR2 is available in this case ;)
Why do you need both TMR0 and TMR2 for your project?

Kyle-s4h said:
What if TMR2 is taking care something else?
It depends on how you're using the timers. For example, if you need a Short_Delay and a Long_Delay, you can use only TMR2 to generate both.
Or go for a PIC with TMR3... :D
 
Actually, in my case, I'm using TMR0 for a delay in my piezo control -- however, it is DISABLED... I'm just polling the overflow bit as I don't need an actual interrupt. This routine was one that I grabbed from piclist, so it could be rewritten to not use TMR0, I just haven't done so.

TMR2, in my case, controls the refresh rate of a 4 digit, 7 segment LED -- and it is interrupt driven.

So, yes, in my case I could probably re-write it and move to TMR2 -- but I still don't agree with the process in general -- I like to FIX things, not forget about them. :) I just like to have an understanding of WHY things aren't working as it helps me fix and/or avoid pitfalls in the future.

Now, back to the topic at hand... I'm scouring the datasheet trying to see if I see something that you (sebana) may have missed. One thing I did notice right away (but I'm not a C guy!) was that bit 3 of the OPTION_REG does not seem to be set (according to the datasheet, it is set to 1 by default (which sets the prescaler to the WDT and not TMR0). Just a thought! :)
 
Kyle-s4h said:
I like to FIX things, not forget about them. :) I just like to have an understanding of WHY things aren't working as it helps me fix and/or avoid pitfalls in the future.
Well, I agree but using a better and simpler timer doesn't mean forgetting problems - I would say that it's a wise choice :). If I have to use 2 timers, I prefer TMR2 and TMR1 (have you considered TMR1?) rather than TMR0. I sometimes wonder why it is still present in modern PICs (backwards compatibility, I guess?)
 
eng1 said:
Well, I agree but using a better and simpler timer doesn't mean forgetting problems - I would say that it's a wise choice :). If I have to use 2 timers, I prefer TMR2 and TMR1 (have you considered TMR1?) rather than TMR0. I sometimes wonder why it is still present in modern PICs (backwards compatibility, I guess?)

I agree with you, TMR0 is a pretty naff timer, and is only still present for backwards compatibility.

However, in this case I doubt that is the problem?, perhaps more down to what code the compiler is producing?.
 
Kyle-s4h said:
Actually, in my case, I'm using TMR0 for a delay in my piezo control -- however, it is DISABLED... I'm just polling the overflow bit as I don't need an actual interrupt. This routine was one that I grabbed from piclist, so it could be rewritten to not use TMR0, I just haven't done so.

TMR2, in my case, controls the refresh rate of a 4 digit, 7 segment LED -- and it is interrupt driven.

So, yes, in my case I could probably re-write it and move to TMR2 -- but I still don't agree with the process in general -- I like to FIX things, not forget about them. :) I just like to have an understanding of WHY things aren't working as it helps me fix and/or avoid pitfalls in the future.

Now, back to the topic at hand... I'm scouring the datasheet trying to see if I see something that you (sebana) may have missed. One thing I did notice right away (but I'm not a C guy!) was that bit 3 of the OPTION_REG does not seem to be set (according to the datasheet, it is set to 1 by default (which sets the prescaler to the WDT and not TMR0). Just a thought! :)

thanks guys, and kyle! i'm appreciating the participation in this discussion alot..

kyle, ive tried tuning the prescaler to WDT, but it didn't work too.. i'm really not understanding this prescaler thing well (and if setting it on TMR0 affects the ADC F-osc..) These frequency thing may well be the thing killing the whole thing!

eng1: i'd be using TMR1 initially, but that goes on to set up one of the pins on the 16877, which another of my module is already using..

Of course I'd like to move over to TMR2 if possible, but considering I've been brooding over these for 2 days, it'll be great to know what's wrong exactly.. It's a simple enough code (for sure) but the problems it brings up is weird!

thanks for the help everyone, i'd keep trying whatever you all suggest!
 
Last edited:
eng1 said:
Well, I agree but using a better and simpler timer doesn't mean forgetting problems - I would say that it's a wise choice :). If I have to use 2 timers, I prefer TMR2 and TMR1 (have you considered TMR1?) rather than TMR0. I sometimes wonder why it is still present in modern PICs (backwards compatibility, I guess?)


Now that's the kind of stuff I like to read! You give a reason / explanation and a solution -- that I understand! ;)

Rep points to you...

(Nigel, your comments are appreciated as well!)

To answer your question -- yes, I have thought about getting the piezo off of TMR0 -- the way the author set it up was to allow for "musical notes" to be generated with varying length and frequency. I don't really need all that as I use a "beep", "beep-beep" and a long "beeeeeeeep" all of the same frequency (just duration changes) -- I'm pretty sure I can do it without a TMR at all, but my standard Delay routine.

@sebana -> just another thought (maybe grasping ;) ), what about the order of initialization... what about initializing the ADC first, then TMR0?
 
Kyle-s4h said:
@sebana -> just another thought (maybe grasping ;) ), what about the order of initialization... what about initializing the ADC first, then TMR0?

Was thinking the same. Also try to use TMR0 without ADC. No way to know what is truely being set and cleared for the registers until you provide an output file in .asm. This sounds like the ADFM format bit is being changed somewhere in the code either by your left shifing or before your left shifting of ADRESL bits.

Why is ADRESH being shifted 8 times as opposed to 6 times?
 
donniedj said:
Why is ADRESH being shifted 8 times as opposed to 6 times?

The ADC high byte must be shifted by 8 bits. Then the low byte is added. The result is stored into a 16-bit variable.

For example:
ADRESH = b'00000010'
ADRESL = b'11110000'

result = 0
result = ADRESH = b'0000000000000010'
result = result << 8 = b'0000001000000000'
result += ADRESL = b'0000001011110000'
 
Is it possible the TMR0 is triggered in the middle of this shifting? Try setting a START_OF_SHIFT flag before shifting and clearning it after shifting, then when the interrupt occurs, check if this flag is set or cleared.

Also disable TMR0IE during critical computations, then set it back up and see what happens.
 
donniedj said:
Is it possible the TMR0 is triggered in the middle of this shifting?
Yes, it is possible (if interrupts are enabled, of course) but I would expect a correct result. ISRs written in C take care of context saving and restoring.

donniedj said:
Also disable TMR0IE during critical computations, then set it back up and see what happens.
I am not sure about the exact function of the ISR in the program but, as you said, TMR0 interrupt may be disabled during A/D conversion and LCD operation. We should see the entire program. I don't see any minimum delay after A/D is turned on or after a new channel is selected?
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top