Unpredictable behavior from my TIny25

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

Hi guys, I got a problem with my ATTiny25V. I'm just trying to blink an LED, but I'm trying to adjust the frequency. I've tried using delay_ms from delay.h, as well as just using a for loop that does nothing between turning portB on and off.

First of all, using delay_ms(1000) doesn't give me a delay of 1 second. It's more like 1/2 second or 1/3 second. Then when I go ahead and increase it to 20000 or 40000 or 60000, the period for all of those is about 1 second. You would think it would increase proportionally, but no. I don't understand why.

I assumed that it was just some reason I didn't understand having to do with the coding of delay.h, but even with a for loop set to do nothing for 1 second's worth of clock cycles (977 with the 1 MHz system clock divided by 1024), the frequency still acts unpredictable.

I attached the code of what I'm trying to do. Can anyone tell me why it's not flashing at 1 Hz?

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
PINB = 0b00010010;    // Turn on LEDs on PB1 and PB4
delay (on_off_cycles, 1); // wait "on_off_cycles" cycles (1/2 second here)
PINB = 0b00000000;    // Turn off LEDs on PB1 and PB4

You probably want to be writing to PORTB, not PINB.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Maybe try something like this:

#define F_CPU 8000000UL // OSC = 8 MHz


#include 
#include 

int x[5] = {~0x01, ~0x02, ~0x04, ~0x08, ~0x10};


int main( void )
{
  PORTC=0xFF;
  DDRC=0xFF;
 
  for(;;)
  {
    PORTC= x[0];
    _delay_ms(1000);
    PORTC= x[1];
    _delay_ms(1000);
    PORTC= x[2];
    _delay_ms(1000);
    PORTC= x[3];
    _delay_ms(1000);
    PORTC= x[4];
    _delay_ms(1000);
  }
} 

Make sure you do "#define F_CPU" with the correct frequency (976.5625 in your case). If I run the same code without defining the F_CPU then my LEDs don't stay on for one second, they are a lot quicker.

(I was using the internal 8 MHz RC oscillator in the code above, which is why I defined the F_CPU as 8000000UL)

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

Someone once told me that setting the bits in PINB as opposed to PORTB used less memory or something. I originally did have it as PORTB.

Anyway though, I tried putting #define F_CPU 977, but it didn't change anything. I even tried another program where I hadn't set any prescalers and defined #define F_CPU 1000000UL (the CKDIV divide by 8 fuse was set) but I still get a shorter period than 1 second per flash.

I really don't understand why my code doesn't give me an actual 1 second period. I mean, there's 977 cycles per second, and I tell it to loop and do nothing 977 times. That's 1 second isn't it? So what kind of black magic is going on in my AVR that makes it less than 1 second?

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

Quote:
Someone once told me that setting the bits in PINB as opposed to PORTB used less memory or something.

Someone told you incorrectly. On newer AVRs writing a 1 to a bit in the PINx register will toggle the output value, but that is not what you want to do here.

Quote:
I really don't understand why my code doesn't give me an actual 1 second period. I mean, there's 977 cycles per second, and I tell it to loop and do nothing 977 times.

First, telling it to do nothing 977 times will not result in 977 clock cycles, simply because you are not telling it to do nothing. You are telling it to increment a counter, check the value of that counter, and loop if the value is not correct 977 times. That would take several cycles per loop. Second, if you have optimization turned on, the compiler sees that you are doing nothing, and so removes the code entirely, which means the delay takes no clock cycles.

Quote:
I don't understand why.

Perhaps if you read the documentation of _delay_ms().

Regards,
Steve A.

The Board helps those that help themselves.

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

so you're saying the code this guy posted wouldn't work?

He posted it back in 2001, were things different then with AVRs?

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

Quote:

so you're saying the code this guy posted wouldn't work?

Not on a Tiny25--'cause it didn't exist then. ;)

Re "this guy": the points made above apply. Roll-your-own timing routines are subject to many "gotchas", including compiler optimization.

The mainstram compilers all have serviceable delay routines that, when properly configured, should give you reliable operation. In any real application none of us are going to rely on it for any timekeeping or repetitive operation or the like, as then we can't be doing anything else for those many ms (or even us). So "fairly close" or "serviceable" is just fine.

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

That guy was incorrect in his assessment. A loop like that would never have been 1 clock per iteration of the loop in any AVR or any version of avr-gcc. The loop could very well have produced a delay, but even now if you don't use optimization that is the case. But delay loops like this are never a good idea. The C language knows nothing about time. Even if you force the loop to create a delay and you manage to get a delay period that you want, the amount of delay might change drastically if you change compilers, or even change versions of the compiler.

Regards,
Steve A.

The Board helps those that help themselves.

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

thanks guys. I figured out how to do it. I used _delay_ms like this:

 PORTB    = 0b00010010;    // Turn on LEDs on PB1 and PB4
         for (blink_short_count = 0; blink_short_count < on_off_cycles; blink_short_count++)
            {
            _delay_ms(100); // wait 1/10 second here
            }
         PORTB    = 0b00000000;    // Turn off LEDs on PB1 and PB4
         for (blink_short_count = 0; blink_short_count < on_off_cycles; blink_short_count++)
            {
            _delay_ms(100); // wait 1/10 second here
            }

It worked perfectly. :)

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

Quote:

PORTB    = 0b00010010;    // Turn on LEDs on PB1 and PB4


That comment is not quite right. Sure it sets bits 1 and 4 to ON but at the same time you are forcing bits 0,2,3,5,6,7 to OFF. You may want to investigate the 101 thread in the Tutorial Forum and consider using:

PORTB    |= (1<<PB1) | (1<<PB4);    // Turn on LEDs on PB1 and PB4

which does EXACTLY what the comment says - the other 6 bits are not touched.

Cliff

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

I see. Thanks, I didn't know there was a difference.

So to turn them off, would I write

PORTB &= ~(1<<PB1) & ~(1<<PB4); // Turn off LEDs on PB1 and PB4
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd use:

PORTB &= ~((1<<PB1) | (1<<PB4)); // Turn off LEDs on PB1 and PB4