attiny25: Cannot vary timer frequency in CTC mode

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

I was attempting a timer tutorial (https://www.avrfreaks.net/forum/t...)  with my atTiny25. To program, I'm using Atmel Studio 7 with AVRISP mkII. My goal is to toggle PB3 via CTC Timer mode on Timer 1. To change frequency, I believe I'm supposed to change OCR1A according to the following formula:

 

OCR1A Count = [(System Clock / Prescale) / Target Frequency] - 1

For example, given 1MHz system clock (default for atTiny25, pg 30 on datasheet) and timer prescale of 1, a target freq of 3906 Hz would give a count of approx 255. However, after checking PB3 on an oscilloscope, the frequency comes up to 62.5 kHz (screenshot attached). In fact, changing OCR1A to anything yields the same frequency. I know it's probably something really simple that I'm missing, but I would appreciate the help.  

 

#define F_CPU 1000000UL

#include <avr/io.h>
#include <avr/interrupt.h>

int main (void)
{
	DDRB |= (1 << PB3); // Set PB3 as output
	
	TCCR1 |= (1 << CTC1); // Configure timer 1 for CTC mode
	TCCR1 |= (1 << CS10);	// prescaler /1
	
	OCR1A   = 255; // Set CTC compare value. Should be ~3906 Hz

	for (;;)
	{
		if (TIFR & (1 << OCF1A))
		{
			PORTB ^= (1 << PB3); // Toggle
		}
	}
}

 

Attachment(s): 

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

You are not clearing the flag.

OCF1A is cleared, after synchronization clock cycle, by writing a logic one to the flag.

 

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

While you can poll the flag state maybe you actually want to try with an ISR?

#define F_CPU 1000000UL

#include <avr/io.h>
#include <avr/interrupt.h>

int main (void)
{
    DDRB |= (1 << PB3); // Set PB3 as output
    
    TCCR1 |= (1 << CTC1); // Configure timer 1 for CTC mode
    TCCR1 |= (1 << CS10);	// prescaler /1
    TIMSK |= (1 << OCIE1A); //interrupt on compare
    
    OCR1A   = 255; // Set CTC compare value. Should be ~3906 Hz

    sei(); // switch on interrupts
    
    for (;;)
    {
    }
}

ISR(TIM1_COMPA_vect) {
    PORTB ^= (1 << PB3); // Toggle
}

The vector to the ISR auto-clears the flag each time.

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

And besides the flag issue:

When the CTC1 control bit is set (one), Timer/Counter1 is reset to $00 in the CPU clock cycle

after a compare match with OCR1C register value.

Stefan Ernst

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

Thanks for everyone's help. I had to give the oscilloscope back, so I won't be able to test out new code until I get my logic analyzer this weekend. I will update with results then.

 

clawson wrote:

While you can poll the flag state maybe you actually want to try with an ISR?

 

Honestly, that was the next step. I wanted to do the non-ISR first. I will try the ISR now though

 

New code to try:

 

#define F_CPU 1000000UL

#include <avr/io.h>
#include <avr/interrupt.h>

int main (void)
{
	DDRB |= (1 << PB3); // Set PB3 as output
	
	TCCR1 |= (1 << CTC1); // Configure timer 1 for CTC mode
	TCCR1 |= (1 << CS10);	// prescaler /1
	
	OCR1C   = 255; // Set CTC compare value. Should be ~3906 Hz
    OCR1A = 255;    //compare with OCR1C
    
    TIMSK |= (1 << OCIE1A); // Enable CTC interrupt
	sei(); //  Enable global interrupts
    
	for (;;)
	{
		
	}
}

ISR(TIMER1_COMPA_vect)
{
	PORTB ^= (1 << PB3); // Toggle the LED
}

 

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

mjgreen wrote:
so I won't be able to test out new code until I get my logic analyzer this weekend.

If you slow things down with a higher timer prescaler and/or system clock divisor, then you can test with an LED.

 

BTW, what is the purpose of exploring the pathological edge conditions, versus something straightforward?  Timer1 on a Tiny25 is an 8-bit timer.  What is the purpose of using CTC with a TOP of MAX?  And a compare match on MAX?  I'd have to think through that one myself.  Start with normal mode with timer clock slow enough for sedate LED blink counting, working with overflows.

 

I suppose if you are aiming for varying frequency in the end then starting with CTC makes sense.

 

 

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.

Last Edited: Thu. Feb 8, 2018 - 07:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

mjgreen wrote:
so I won't be able to test out new code until I get my logic analyzer this weekend.

 

I suppose if you are aiming for varying frequency in the end then starting with CTC makes sense.

 

Yeah, that's my eventual goal.

 

With a few trials, the following code does work. I can go as low as 77 us periods (I did up to 3 seconds as well). However, I would like to reach 22 us (around 42 kHz). Changing OCR1C = 11 should be able to do this, but my period won't go lower than 77 us. I'm not sure what is stopping me.

 

#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h>

 void initTimer(void)
{
	TCCR1 |= (1 << CTC1);  // clear timer on compare match
	TCCR1 |= (0 << CS13) | (0 << CS12) | (0 << CS11) | (1 << CS10); //clock prescaler = 1
	OCR1C = 50; // match value, where period = ((match * prescaler)/1000000) * 2. Prescaler and match currently set for 100 us
	TIMSK |= (1 << OCIE1A); // enable compare match interrupt
}

ISR(TIMER1_COMPA_vect)
{
	PORTB ^= (1 << PB3); // Toggle the LED
}

int main(void)
{
	// initializations
	DDRB |= (1 << PB3); // Set PB3 as output
	
	initTimer();        // initialize timer registers
	sei();               // enable interrupts
	
	while(1)
	{
	}
	
}

 

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

mjgreen wrote:
but my period won't go lower than 77 us. I'm not sure what is stopping me.

What speed is your AVR really running at?  How have you proven that?

 

What optimization level have you told the compiler to use?  What is the generated code for the ISR?

 

What do you get when you allow the timer to toggle e.g. OC1A pin (and no ISR)?

 

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

theusch wrote:

 

What speed is your AVR really running at?  How have you proven that?

According to the data sheet (pg 30), the attiny uses a 8 MHz system clock. The default prescale divider for this clock is /8, resulting in 1 MHz clock. I proved this using the _delay_ms function:

 

#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int main(void){
	DDRB |= (1 << PB3); // Set PB3 as output
	
	while(1){
		 _delay_ms(1);
		PORTB ^= (1 << PB3); // Toggle the LED
	}
	return 0;
}

This accurately gives me a 2 ms period, according to my logic analyzer. Of note, the _delay_us function seems to be off by 5 us. So doing _delay_us(6) delays for 11us, giving a period of 22 us.

 

Quote:
What optimization level have you told the compiler to use?  What is the generated code for the ISR?

Optimization is at default at -O1. What do you mean by generated code? I'm not sure where to look for that.

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

mjgreen wrote:
I'm not sure where to look for that.

It's your toolchain; I'm not an expert.  .LSS I think.

 

Anyway,

mjgreen wrote:
I can go as low as 77 us periods

so if the generated code takes more than 35 or so cycles for each ISR invocation, then that would explain your symptoms.  Same results at 8MHz?

 

mjgreen wrote:
Of note, the _delay_us function seems to be off by 5 us.

I'd wager delay_ms() is not "off".  Rather, your AVR's clock may well be "off".  What does the datasheet say about the accuracy?

 

In addition, have you considered that the toggle operation and the while(1) iteration takes some cycles?  have you looked at the code to count the cycles?  If RJMP is two cycles and your forced RMW sequence is an IN/EOR/OUT for three or maybe four, and your clock ticks are 1us each, what does that say about your loop times?  What results do you get in the Studio simulator for cycle counts and elapsed time?

 

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.