Determining value of INT0

Go To Last Post
36 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a program in done in c that I am trying to convert into assembly. I am using the tiny26. For interrupt 0, I have will have two different routines, depending on whether the edge was rising or falling. For the moment, I am just toggling an led. What would be the best way to determine whether the interrupt is a one or zero. The code I have been using is:

receive_isr:
	push reg1
	push n
	push curr_bit
	push BITS_ctr

        ldi bus_busy,1

	sbis PINB,6
	rjmp NOT_PINB
            ldi reg1,(1<<PA0)
	    out PORTA,reg1

	    ldi reg1,0x02
	    out MCUCR,reg1
            rjmp done_isr
	NOT_PINB:
	    ldi reg1,(0<<PA0)
	    out PORTA,reg1

	    ldi reg1,0x03
	    out MCUCR,reg1
done_isr:
	pop BITS_ctr
	pop curr_bit
	pop n
	pop reg1
reti


MCUCR was initialized to catch rising edges, INT0 is enabled in GIMSK and all of the appropriate .defs have been made. I can get the led to trigger on, but can't get it to shut off if I take away the 5v source

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One potential problem (in the future), since this is an ISR, you need to be preserving SREG. Though in this case SREG is unaffected by any of your operations. However when you start doing more with your ISR, you will run into trouble, unless you preserve SREG. (it's obvious you plan on doing more, as you are preserving a bunch of registers you currently do not touch)

How are you triggering INT0? If with a switch, do you have debounce sufficient circuitry? With the switch bounce, you may actually be falling out of sync with the signal, and executing the rising code, on a falling edge interrupt, or vice versa, and thus configuring for the next interrupt incorrectly. For now, leave MCUCR alone in the ISR, and run your code, with MCUCR to trigger on any edge (0x01) to see if that works.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

at the moment it is just a manual trigger of touching a wire with 5v to the int0 pin. I have also tried to hook it up to the unit it will be communicating with to see if it toggles, but it remains high.

I tried it with MCUCR triggering on any edge and got the same result.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you only intend to control a single LED on PORTA then consider using CBI/SBI. This way you will not be writing values to all the PORTA pins when all you want to do is control the LED.

cbi PORTA, PA0 ; set PORTA0 to zero
sbi PORTA, PA0 ; set PORTA0 to one

Notice that CBI/SBI do not use the shift operand (i.e. 1 << PA0). They just use the port pin designator as shown in the examples above. This is documented in the assembly language reference.

Is there someplace in your main program code outside the interrupt service routine where you are writing to some other PORTA pin with an OUT instruction and turning the LED on by accident? Is your LED turned on when PORTA0 is a one value or a zero value (many development boards will turn the LED on with a zero value)?

Since you are using a mechanical connection to switch INT0 then it needs to be debounced. Bouncing contacts or connections can cause the state of PINB6 to change between the interrupt event and when the interrupt reads the state of PINB6. Bouncing signals change very rapidly which means you could trigger on a rising edge “one” value and read an opposite “zero” value when the interrupt response program code runs. Bouncing signals could confuse your interrupt routine. You will also probably get a flurry of repeated INT0 interrupts for a single change in the wire connection, until the bouncing stops. Debouncing can be done in software or with external hardware.

You could also try using “any change on INT0 generates an interrupt request” in MCUCR. Then your interrupt routine would not have to change MCUCR. The way the interrupt currently writes to all the MCUCR control bits would make it impossible to use the pull-up disable or setup the sleep modes when the INT0 interrupt is enabled.

I see you must intend to do more with this interrupt in the future because you are pushing and popping registers that the interrupt code does not use. I suppose you know that whatever register bus_busy is assigned to will be wiped out when the INT0 code runs. When accessing bus_busy outside the interrupt in your main code, you should temporarily disable global interrupts or just INT0 if you want to prevent the INT0 interrupt from messing with bus_busy while you are reading or resetting it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did not know that about bus_busy, but thanks for pointing it out. If I am going to clear bus_busy before I exit my routine, would I need to disable the interrupts and set it or is the way I have it ok? I am just using it to flag whether or not it is ok for me to send something over the bus. It is only changed in the isr, but is being compared in another routine

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are two kinds of external interrupt detecting hardware in AVRs, so there are two possibilities. Or actually three.

The first kind of hardware can be configured to be sensitive to one edge at a time, either rising or falling edge, but not both edges. So if you configure the hardware to a rising edge and get an interrupt, then you are sure it is a rising edge. The edge must then be changed within the interrupt routine to get the next edge, which is the falling one. The state can even be memorized in a variable, and toggled with xor operation. I've used this and it works great.

The second kind of hardware can be configured to be sensitive to both edges. You can read the state of the interrupting signal in an interrupt routine if you are sure the signal does not change before you can read it, or if you are sure you can process the interrupts so fast that you do not miss any of them, you can just keep the state in a variable.

Third option is to use two interrupt pins for one signal, one configured for rising and other to falling edge. Then there can be totally different interrupt routines without deciding what to run.

All this how to do things depend on your signal. As you do not tell us what the final source and speed of the signal will be, we cannot tell if it is even worth to use interrupts to detect edges in your signal :)

- Jani

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am not writing to PORTA anywhere else in the program, I did find that my DDRA was screwed up. I fixed that, hooked it up to the bus (no button) and it seemed to work. When I put what is actually going to be executing into the routine, the port began acting the same way, it would turn on at the beginning when it saw the line go high, but wouldn't come back down. I am only altering the state of porta in the same two locations

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using the tiny26 at 12 mhz. I will be clocking waves with a minimum length of 20us and maximum of around 34us. I am using TCNT0 to clock the lengths and record that value into a buffer on the falling edge of the waves. I am reseting the value of TCNT0 on the rising edges

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What I meant is since you do not push and pop bus_busy, its value will change to a 1 anytime the INT0 interrupt code runs. From the point of view of the non-interrupt main program code this change to a one value will appear at random since it is controlled by the external interrupt.

Since you are using bus_busy elsewhere, you may use it like you are doing now, as a global variable or global flag set by the interrupt. This is a perfectly legitimate way to use bus_busy as long as you remember it can change suddenly.

If your non-interrupt main program code reads bus_busy, sees a value other than 1 and acts on it, you have to keep in mind if INT0 is enabled it might sneak in immediately change it just after you read busy_busy. So, if you are using bus_busy as a kind of control gate of flag you need to be aware that it can change suddenly as long as INT0 is enabled (both the local INT0 enable and global interrupts enable together). This sudden change characteristic can confuse the non-interrupt main program code design if you forgot it could happen. Obviously, one easy way to manage this is to temporarily turn off INT0 or global interrupts. That way INT0 cannot change bus_busy until INT0 is operational again after the temporary delay. On the other hand your main program design might not even care meaning you do not have to manage it at all.

BTW, you did not say if your LED turns on with a PORTA0 zero or one value. I'm wondering if you are seeing the LED turn on and getting the wrong idea about the PORTA0 value. Any confusion here would impact your interrupt program code design. Your real problem could possibly be that PORTA0 is turned off and cannot be turned on. I'm only guessing, but I have seen simple misunderstandings like this cause great confusion and cost lots of debugging time and effort.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It turns on when I write a one to it. Here is my isr, it will probably be more help than I can explain at this point. The reason for two different TCNT0 lengths is due to the fact that there is a start bit of 174us at the start of every packet. after that, every other bit is 40us long, either being 20/20 or ~34/6

receive_isr:
	push reg1
	push n
	push curr_bit
	push BITS_ctr

	;********************************************
	;CPU Frequency 		= 12000000				*
	;Prescaler			= 64					*
	;Periods Desired	= 172us,40us			*
	;so 12000000/64		= 187500 ticks/second	*
	;need .000192(sec) * 187500 ~= 36 ticks@	*
	;need .00004(sec) *187500 ~= 7 ticks@		*
	;		@ = TCNT0 value to check for		*
	;********************************************
	ldi bus_busy,1

	sbis PINB,6
	rjmp NOT_PINB
;	{
		sbi PORTA,PA0
	;	ldi reg1,0x02
	;	out MCUCR,reg1
	
		ldi reg1,0x00
		out TCCR0,reg1

		cpi n_byte,0
		brne else
			ldi reg1,220
			out TCNT0,reg1
			rjmp no_else
		else:
			ldi reg1,249
			out TCNT0,reg1
		no_else:
		ldi reg1,0x0b
		out TCCR0,reg1
;	}
	rjmp done_isr
	;END OF PINB & 0x40

	NOT_PINB:
		cbi PORTA,PA0

;		ldi reg1,0x03
;		out MCUCR,reg1

		ldi reg1, 0x00
		out TCCR0,reg1

		cpi n_byte,0
		brne else2
			ldi reg1,TCNT0
			cpi reg1,240
			brlo out1				;if TCNT0 > 240
				inc bus_byte
				rjmp out1
		else2:
			ldi reg1,TCNT0
			cpi reg1,253			
			brge out1				;if TCNT0 < 253
				inc bus_byte
		out1:
		inc n_bit
		
		cpi n_bit,4
		brne shift_left
	;	{
			ldi n_bit,0
			;****************************************
			ldi XL, LOW(R_BUS)
			ldi XH, HIGH(R_BUS)			
			add XL, n_byte				;offset
			st X, bus_byte 				;store it
			;****************************************

			ldi bus_byte,0
			cpi n_byte,15				;calculate size of packet
			brne not_size
	;		{
				ldi reg1,18
				add size,reg1
				ldi reg1,R_BUS+15
				add size,reg1
				add size,reg1
				add size,reg1
				add size,reg1
	;		}
			not_size:
				inc n_byte
		rjmp check_size
	;	}
		shift_left:
			lsl bus_byte

		check_size:
		cp n_byte,size
		brne done_isr
		{
			ldi reg1, 0x00
			out TCCR0,reg1
			ldi n_bit,0			
			ldi n_byte,0
			ldi bus_byte,0
			ldi bus_busy,0
			mov size, reg1
			ldi reg1,1
			mov received,reg1
		}								;END (!(PINB & 0x80))

done_isr:
	pop BITS_ctr
	pop curr_bit
	pop n
	pop reg1
reti									;END SIG_INTERRUPT0 HANDLER

overflow0_isr:
	push reg1
	ldi reg1, 0x00
	out TCCR0,reg1
	ldi n_bit,0
	ldi n_byte,0
	ldi bus_byte,0
	ldi bus_busy,0
	mov size, reg1
	ldi reg1,1
	mov received,reg1
	pop reg1
reti
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OZ1 wrote:
I am using the tiny26 at 12 mhz. I will be clocking waves with a minimum length of 20us and maximum of around 34us. I am using TCNT0 to clock the lengths and record that value into a buffer on the falling edge of the waves. I am reseting the value of TCNT0 on the rising edges

That's 50kHz, quite fast, may I ask what is it that you are doing and decoding? Could any extra hardware assist you in decoding the bits?

But OK, you might want to check out if your AVR has a thing called timer capture. It will help you out a bit. I think it can be done.

- Jani

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is going to be for a cd changer emulator. I am using a prescaler of 64 with the oscillator, the decoding has to be done in software. There is no input capture as near as I can see from looking at the register summary. I have this program done in c, but I am not sure how to do the check for a one or zero on int0. The line in c is:

if (PINB & 0x40)

and then the 'not' of that to see if it is zero

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OZ1 wrote:

if (PINB & 0x40)

There must be a gazillion of ways of reading PB4 bit.
Here's one.

;; read PINB port to r16
in r16,PINB ;; PINB=read register of port B data
;; skip if bit 4 in register r16 is set
sbrs r16,PB4 ;; PB4=define for bit 4
rjmp pin_is_low_subroutine
rjmp pin_is_high_subroutine

- Jani

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OZ1 wrote:
I have a program in done in c that I am trying to convert into assembly.

C compilers usually do a pretty good job of that don't they? :lol:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I thought about that and looked at the list file, seemed very inefficient on several things

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:
One potential problem (in the future), since this is an ISR, you need to be preserving SREG. Though in this case SREG is unaffected by any of your operations. However when you start doing more with your ISR, you will run into trouble, unless you preserve SREG. (it's obvious you plan on doing more, as you are preserving a bunch of registers you currently do not touch)

OZ1: Note that your receive_isr routine definitely violates the sanctity of SREG. As such, you really must save/restore it along with the rest of the pushes and pops. Otherwise, you'll find the logic of the main (non-interrupt) portion of the program will start going haywire.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

how do i preserve sreg?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Something like this might work: (replace your existing ISR entry/exit)

receive_isr: 
   push reg1 
   in reg1, SREG
   push reg1
   push n 
   push curr_bit 
   push BITS_ctr 

...

done_isr: 
   pop BITS_ctr 
   pop curr_bit 
   pop n 
   pop reg1
   out SREG, reg1
   pop reg1 
reti
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That was the problem, thanks for the help

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the future, is there a way to tell if that will happen or if it will happen to any other registers i might need to preserve?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Most of the time, you will know which registers are modified (and thus need to be preserved) because they usually only change if you explicitly name them somewhere in the body of the code. Any registers that are used by both the ISR and by the main program, will need to be saved.

(An important exception to this is the situation where the ISR is actually using changes in certain registers to signal some important change in state to the main program. SREG will almost never be used for that purpose.)

SREG is a special case in that your code most likely never explicitly references it, but it is central to the operation of almost every AVR instruction. This is the register that holds the results of any comparisons, aritimetic or logic you perform. (For example, if you add 255 + 2, you'll get an overflow, and the overflow flag in SREG will be set.)

These flags are important; they are the basis of all the decisions that are made by the conditional branch instructions. And that is the basis of determing the flow of the program.

In general, if it is possible for an interrupt to happen in between a logical operation, and its subsequent conditional operation, then care must be taken within the interrupt to make sure that SREG's original value is restored just before the interrupt exits. Otherwise, the decision that is made after the interrupt returns will be based on bogus information, and just about anything could happen.

A complete breakdown of which op-codes will affect SREG, and how SREG reacts to those op-codes is included in the AVR Instruction Set document on Atmel's website or in AVR Studio's AVR Assembler help files.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was wondering if you would help me figure this out, I have the attached wave form, the first wave on the upper line is what I am receiving, the second wave is what I am sending. The lower line is a porta line that is supposed to be toggling with the receiving wave. When I don't send anything upon receiving, it toggles just fine, I thought it might be a problem with bus_busy as you said it could cause issues. I changed my code and disabled interrupts before I set that value, then re-enabled them after I had set it, but got the same result. Any ideas on what might be causing this?

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OZ1,

I think you would have an easier time stepping back and creating another throwaway assembly project just to debug this problem. Make a disposable copy of your project and strip it down to the essential basics of INT0 and PORTA0 (like you did with the receive_isr example code at the beginning of the thread). This will allow you to focus on this one aspect of your hardware interface.

Your logic analyzer picture does not give the sample timing per division information and you never said exactly what you meant by “the first wave is what you are receiving”. I will assume your top line is INT0 input and the bottom line is the PORTA output (specifically PORTA0). Since INT0 is a simple mechanical wire connection I will assume the rapidly changing signal is the switch bounce as seen by the detection threshold of the analyzer which interprets it as a square wave (it is actually a quite nasty digital analog mixed waveform until it settles down).

First, do you have a pull down resistor on INT0 and is the INT0 pin pull up enabled? If you are connecting a wire to +5 and then removing it from +5 volts without a pull down resistance then one of two things is happening. Either you are paralleling an AVR internal pull up with +5 which will not change anything or you are alternately applying +5 to a high impedance AVR pin with no known value which will not have any predictable effect. BTW, just because the logic analyzer sees the signal changing does not mean the AVR sees it the same way, unless the logic analyzer has the exact same logic switching threshold voltage as the AVR input pin. An analog scope and the AVR data sheet would be required to figure out what the AVR is seeing on INT0. If your looking at switch bounce then it will have to be a good analog storage scope (a less than 20 MHz audio scope would not be useful and bouncing is a temporary event requiring some way of storing it). As an alternative you could use software to make an ISR for INT0 that will only toggle the PORTA output pin value (no reading or any other processing, just toggle the PORTA0 pin value) and put this simplified PORTA0 output on your logic analyzer. This will cause every other INT0 event (as the AVR chip actually sees it) to toggle the PORTA0 output pin. As a logic analyzer display of INT0 and PORTA0 it will tell you exactly what is happening with INT0 and the interrupt response.

I predict the first thing you will learn is that none of us were kidding when we said you must debounce the INT0 input.

You can use this simplified project to work on getting INT0 to function the way you want it to.

Sorry, I did not think about INT0 needing a pull down before. I have to assume so much from simple written descriptions that I overlook basic things sometimes. If you do not want to do a pull down, then enable the AVR INT0 internal pull up resistor and connect your INT0 switch wire to ground instead.

You have another option. For testing purposes you could setup an AVR timer output and hook it to INT0. This would give you a controllable clean INT0 signal without any bouncing. Since you have a logic analyzer you will be able to see what is happening on INT0 and PORTA0 without any difficultly.

After you figure out what is going on using your simple throwaway project, go back to your real project and make the needed changes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

You have another option. For testing purposes you could setup an AVR timer output and hook it to INT0. This would give you a controllable clean INT0 signal without any bouncing. Since you have a logic analyzer you will be able to see what is happening on INT0 and PORTA0 without any difficultly.

Excellent suggestion.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The jumbled part isn't bouncing, it's the actual packet that I will be interpreting. I had the logic analyzer hooked up directly to the INT0 pin when I took this log also. I will try what you suggested and strip everything down though. I do have a pull down resistor on the INT0 pin and pullups are enabled. The sample rate is 500khz.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
This will cause every other INT0 event (as the AVR chip actually sees it) to toggle the PORTA0 output pin. As a logic analyzer display of INT0 and PORTA0 it will tell you exactly what is happening with INT0 and the interrupt response.

Ok, did this and waveform is the wave I got, INT0 on top, PORTA on the bottom. It works this way for receiving when just toggling porta when INT0 is triggered.

When I change my main method to respond when something has been received, I get waveform2, again, INT0 on the top and porta on the bottom

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Those didn't turn out very well, waveform two turns porta on when it int0 is first triggered and doesn't shut it off until int0 holds at zero. Waveform toggles the line like it should

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At a 50 KHz interrupt frequency and a 12 MHz AVR clock frequency it means you have 240 CPU cycles per INT0 interrupt event (12,000,000 / 50,000 = 240). However, since you change the INT0 edge detection between rising and falling every cycle you are really doing two interrupts per each 50 KHz cycle. That leaves only 120 CPU cycles. The real killer is the input waveform has a really uneven duty cycle. If you look at just the very short time spent at a zero value compared to the long time spent at a one value, you can see the falling INT0 to rising INT0 period leaves even less time than 120 CPU cycles, allot less. You would need to aggressively trim down the interrupt response code size to a bare minimum for this to even have a chance of working.

Since you gave an input example at 5 volts it would appear your Vcc is 5 volts. This means you could run up to a 16 MHz clock. Changing to 16 MHz would only get you from 120 to 160 CPU cycles, but again it is not a 50 % duty cycle so you will only see a fraction of the increase where you need it the most.

There is another possible solution. Connect INT0 and one of the Pinchange interrupts together. Fix INT0 on a rising edge detect and the Pinchange interrupt will trigger on both edges. Since INT0 has a higher priority, it could just cancel the Pinchange interrupt for the rising edge by clearing the PCIF Pinchange interrupt flag first thing in the INT0 interrupt response program code. Then the Pinchange interrupt would act as a falling edge triggered interrupt. This will eliminate the problem with the uneven duty cycle and each interrupt would have 240 CPU cycles per interrupt event at 12 MHz AVR clock. You would have to split the interrupt handling code into a falling detect (Pinchange) and rising detect (INT0) entry point. After the split entry points you could jump to a common interrupt response program before the RETI. Notice that by using INT0 to cancel the rising edge detect on the Pinchange interrupt puts this handling code during the long part of the duty cycle, where it is not as critical timing wise.

As always, since there is an unavoidable delay between the interrupt event and when you read the INT0 pin, reading a PORT pin value from inside the interrupt response program code is never guaranteed to read the correct pin value. However, with dedicated falling and rising interrupt edges you do not need to read the PORT pin. Its simple, the falling edge interrupt means the PORT is a zero value and the rising edge interrupt means the PORT is a one value at the time the interrupt edge was detected. So, using two interrupt pins means you do not have to read the PORT pin at all.

BTW, unless you need the fast timer PLL, the ATtiny2313 has an INT0 and INT1. It also has a 16 bit timer input capture, can clock up to 20 MHz and has the same number of pins. It might be worth looking at.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is it possible to set INT0 up to just read rising edges and use the same pin on a pinchange interrupt to catch the falling edges or should I use a separate pin?

The reason I ask is that I am going to be switching to a smaller chip since the rest of the circuit takes up a lot of space. The INT0 pin is going to be the same as the PCINT2 pin. I will eventually be switching to the tiny45

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OZ1 wrote:
Is it possible to set INT0 up to just read rising edges and use the same pin on a pinchange interrupt to catch the falling edges or should I use a separate pin?

I don't know the answer to that one. It depends on the internal AVR interrupt hardware design.

All I can suggest is you try in on the ATtiny45 and see what happens. Use a clean slower signal (like a timer output) on the INT0/PCINT2 pin and your logic analyzer to find out if your test works. Then if it works your serial input stream on a single pin should work also. If you need it, the ATtiny45 is another 20 MHz maximum clock speed part. The different pin layout prevents trying this single pin test on the ATtiny26. You can however develop the split INT0 and PCINT interrupt service program code on the ATtiny26.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was reading through the data sheet on the tiny45 and saw that if an external clock is used, T0 can't be prescaled. Why would they rate it at 20mhz if it can't be prescaled

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I could not find where you saw your information in the data sheet. However, on pages 80 and 81 table 13-9 Clock Select Bit Description gives you the choice of a prescaled I/O internal clock or an external clock. The AVR chip is not wired to run the T0 external clock signal through the I/O clock prescaler. It is just the way the ATtiny45 is built.

Page 84 section 14.0.2 External Clock Source details the synchronization circuit used with an external Timer0 clock input (the T0 pin). Because the AVR must access the Timer0 registers with the internal AVR I/O clock timing synchronization in order to work, it requires extra hardware to synchronize an external clock to the AVR I/O clock timing. It states an external T0 clock must be less than AVR I/O clock divided by two when the external clock has a symmetrical 50 % duty cycle. When using the AVR internal I/O clock which is generated from and therefore synchronized with the AVR CPU clock, its a 20 MHz timer part. When using an external T0 clock, the synchronization circuit requires time to perform the synchronization and its less than 10 MHz timer part (assuming you are using a 20 MHz AVR clock with no CLKPR division). I think you will probably find all the AVR timers are this way when external clocks are used to drive the timer. External clocks are asynchronous or in other words not synchronized with the AVR clock edges. Internal AVR clocks run the entire AVR, and ones from a common source are synchronous with respect to each other.

Any complex edge timed synchronous digital system that allows asynchronous clocks to directly drive internal synchronous systems with no synchronization risk metastalility. Metastalility can cause all kinds of failures in various internal circuits. The answer is simple, always require synchronization or some kind of protocol to access the asynchronous clock source systems that prevents metastalility.

OZ1 wrote:
Why would they rate it at 20mhz if it can't be prescaled

The only other possible thing I can think of is not to confuse prescaling which always divides frequencies down to a lower frequency with AVR fast PLL timer clocks. The PLL is used to multiply frequencies up to a higher frequency. Not being able to prescale a clock source actually allows the fastest possible speed from that clock source. If Timer0 had no internal I/O clock prescaler then it would always be a full speed internal clock up to 20 MHz part (depending on the main clock frequency with no CLKPR division being used).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So will i be able to use a prescaler value in TCCR0B with my external crystal for this then?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It sounds like maybe you confused the T0 external clock just for Timer/Counter0 with the main external clock for the entire AVR.

As long as you use the table 13-9 Clock Select Bit Description CS02, CS01 and CS00 bits to select one of the clkI/O selections and not the T0 external timer0 clock pin, your timer0 TCNT0 register will be driven from a prescaled internal I/O clock source. In other words as long as you do not use the timer0 T0 input pin you can use the prescaler in TCCR0B. The prescaler source is from the main AVR clock divided by the CLKPR setting (or external crystal in your case). So, you you can use the TCCR0B prescaler with an external crystal main AVR clock.

BTW, I just noticed the T0 pin is the same one as INT0 and PCINT2 on the ATtiny45. You could not use T0 even if you wanted to.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Got my board and tiny45 finally so I can start testing with it. I went back to my c version of the code until I can get everything working the way I want. For some reason, I can't get the INT0 interrupt to fire on this chip though. I went through the register bits and made sure they were the same, but for some reason can't get it to trigger. I started a thread on it in the avr gcc, but haven't heard anything back yet. Any ideas?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The first thing that comes to mind is going through the ATtiny45 data sheet and checking how the external interrupt registers are supposed to be used. It may not be the same as the ATtiny26.

Next I would make sure you changed the source code device header to the ATtiny45.

Then there are a whole bunch of alternate functions on the PORTB2 pin. There are SCK/USCK/SCL/ADC1/T0/INT0/PCINT2 alternate functions all on the same pin. Go through the data sheet and identify all of them. Make sure that none of these alternate modules are turned on (except the ones you want to use). Be through. For example the DIDR0 Digital Input Disable register is located in the ADC section and would cause problems if the ADC1 input was disabled for digital inputs.

Make sure you set PORTB2 as an input since you are using an external interrupt source.

The ATtiny45 has an ATtiny15 compatibility mode. Anytime you see an AVR chip with any compatibility mode, make sure the compatibility mode fuse is not programmed. If you want to have it programmed then read up on ALL the compatibility mode changes first. Some chips might have a compatibility mode fuse enabled by default like the ATmega128 which comes with the 103 compatibility fuse enabled.

Finally, do not forget to look at the errata section in the data sheet. You can also check the errata sticky thread at the top of the AVR Forum.

Keep all this in mind for the future when you want to try a different AVR chip again.