Problems generating waveforms by polling ATtiny85 TIFR flags

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

I wonder if anyone has experience of polling the ATtiny85 Tiner/Counter1 using the OCF1A, OCF1B, or TOV1 flags in TIFR.

 

I want to generate an accurate symmetrical 50kHz waveform on pin 1 from an ATtiny85 running at 8MHz, and Timer/Counter1 running at 1MHz with the following setup:

void setup() {
 TIMSK = 0;                             // No interrupts
 pinMode(0, OUTPUT);
 // Make Timer/Counter1 count at 1MHz
 TCCR1 = 0<<CTC1 | 0<<PWM1A | 4<<CS10;  // 1MHz timer clock
 GTCCR = 0<<PWM1B;
}

Here's attempt 1, counting TCNT1 up until it matches output compare register A:

void loop () {
  TCNT1 = 0;
  TIFR = 1<<OCF1A;
  OCR1A = 9;
  while ((TIFR & 1<<OCF1A) == 0);
  PINB = 1;
}

However, this gives a waveform that's high for approx. 10msec and low for approx. 2msec. However if I change the code to:

void loop () {
  TIFR = 1<<OCF1A;
  TCNT1 = 0;
  OCR1A = 9;
  while ((TIFR & 1<<OCF1A) == 0);
  PINB = 1;
}

then it works as expected, and I get a symmetrical waveform that's high for 10msec and low for 10msec. What's going on?

 

By the way, please don't tell me that there's a better way to do this. This is a simplified part of a larger project, and I want to understand why I'm getting this behavior.

Last Edited: Fri. Mar 3, 2017 - 01:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you want CTC mode, surely the bit would be 1.
.
David

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

Sorry, the comment was incorrect; corrected now (I don't think CTC mode affects this case).

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

This is a great example to debug using the simulator, have you tried that? 

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

No, unfortunately I don't have that. Where's it available from?

 

I suspect this is a timing issue - how accurately does it simulate that?

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

The simulator is part of AS4/5/6/7

 

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

The code suggests an Arduino sketch.  So there are timers running with who knows what setup, and the loop() calls are at arbitrary intervals.

 

That said, you might just have a race depending on the order of your operations.  (indeed I'd think that a 1MHz timer tick would give you a few microseconds and not be a problem)  What do you get with e.g. 99 instead of 9 for the OCR value?

 

If it were me, I'd have the timer all set up except for the CS bit(s) -- i.e., stopped.  Then I'd clear TCNT and the TIFR flag(s) and set up all the other registers.  THEN set the CS bit(s) to start the timer.  When the needed interval has completed, stop the timer and take action as needed.

 

I've done scores of production AVR8 apps, and very rarely have I set up a sequnce as in your example.  Almost always the timer is set up to do the hard work.  Sometimes it disables itself when the work is done.

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

Yes, it's an Arduino sketch, but I put TIMSK = 0 to ensure no timers are generating interrupts.

 

Putting 99 instead of 9 for the OCR1A value doesn't substantially change the result.

 

Thanks, David

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

Got to repair the empty post...

 

If OCR1A is 0 upon entry to the routine then the IF will be set every cycle or two in CTC mode.

 

Hard to tell without further investigation.

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: Fri. Mar 3, 2017 - 03:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why are you doing this by polling?  Let the timer do it.  PWM or CTC mode would be most appropriate.

 

If you want to untangle what is actually happening, you'll need to look at the .lss.  Since this is Arduino, there isn't one.  Post the .hex file.  That should be in a temporary folder somewhere, probably named 'build<some-long-string>'.  The folder and it's contents are deleted when closing the Arduino IDE.  Verify or Build the sketch, then go looking for the .hex file and post it here.  Better yet, post the .elf file.

 

However, I think it's a fair amount of effort to try to understand why it's not working, when it's already clear that this is the wrong approach to achieving your goal.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

joeymorin wrote:

... when it's already clear that this is the wrong approach to achieving your goal.

 

How do you know? I haven't said what my goal is.

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

How do you know? I haven't said what my goal is. 

But you have:

want to generate an accurate symmetrical 50kHz waveform on pin 1

That screams CTC, not polling.

 

If there's something you're leaving out, perhaps you would be so kind as to fill us in.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Fri. Mar 3, 2017 - 04:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, perhaps that was misleading, although I did add:

This is a simplified part of a larger project

I'm trying to write a simple implementation of the 1-Wire protocol for the ATtiny85. This involves timings like:

 

  • Output low, delay 6µsec, output high, delay 64µsec.
  • Output low, delay 60µsec, output high, delay 10µsec. 

 

I can't see an elegant way of doing it either with interrupts, or using the USI, so polling looks like the simplest solution. 

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

A Google search for "1-wire driver arduino" pulls up several implementations.

 

I haven't dug into the implementations.  This one

http://playground.arduino.cc/Lea...

says in part:

Bitbanging approaches
Where native buffering/clock management is not available, 1Wire may be implemented on a general purpose IO (GPIO) pin, where manual toggle of the pin state is used to emulate a UART/USART with reconstruction of the signal from the received data. These are typically much less processor-efficient, and directly impact and are directly impacted by other processes on the processor shared with other system processes.

On Arduino and other compatible chips, this may be done with the OneWire library (linked above, examples below) on any available digital pin.

I'd think packets are quite short?  No more than a few bytes?  Then I guess I'd turn off global interrupts for the duration.  The GCC toolchain has near-cycle-accurate delay facilities, and the loop overhead for each bit should be close enough to constant.

 

 

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

Have you sought out prior implementations for AVR?  There are several out there.  The bit-banged versions tend to employ cycle-counting rather than polling a timer, but you could use a timer interrupt combined with a pin-change interrupt instead.  I'm not sure how polling the flag of a hardware timer is an advantage, but if you want to approach it this way I would use a 'leap-frog' technique.  Run the timer in normal mode, set OCRnx to some future time.  Then poll the flag, change OCRnx for the next schedule, clear the flag, rinse and repeat.  No need to mess about with TCNTn.

 

Some implementations use the USART.  While the the t85 lacks a USART, it should be possible to implement using USI.  What existing implementations have you explored?  Are you trying to implement a master or a slave?

 

  • Output low, delay 6µsec, output high, delay 64µsec.
  • Output low, delay 60µsec, output high, delay 10µsec. 

I cannot understand how generating a 50 kHz square wave helps you achieve your goal.  That's a 20 us period, and a 10 us half-period.  As your have noted, 1-Wire send timing boundaries are 480 us, 1-15 us, and 60 us.  Receive timing is 30 us.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Thanks for your suggestions.

 

joeymorin wrote:
I cannot understand how generating a 50 kHz square wave helps you achieve your goal.  That's a 20 us period, and a 10 us half-period.  As your have noted, 1-Wire send timing boundaries are 480 us, 1-15 us, and 60 us.  Receive timing is 30 us.

 

That was just a simplified demonstration of the problem that had I had been struggling with. I now have a simple 1-Wire protocol working reliably on the ATtiny85, but I would still like to understand why my earlier attempts didn't work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void loop () {
  TCNT1 = 0;
  TIFR = 1<<OCF1A;
  OCR1A = 9;
  while ((TIFR & 1<<OCF1A) == 0);

If I had to guess - in this scenario I suppose TCNT1 had already counted past 9 by the time I reached the OCR1A line. That is what I think Lee meant by:

theusch wrote:
you might just have a race depending on the order of your operations.

You need to look at the generated code and see what opcodes are involved and how many cycles.

 

Having said that. Even if the clock were running at F_CPU then you'd still have 9 cycles there (I guess) and yet you have:

4<<CS10;

which appears to be /256 so that suggests 2,304 cycles before the count reaches 9. Difficult to see how it could be taking THAT long! (unless, of course, this is coincident with a hugely long ISR being serviced?)

 

EDIT by the way why does the comment say "1MHz"? The Arduino generally runs at 16MHz and 4<<CS10 is surely just 1<<CS12 ? By my maths 16MHz/256 = 62.5kHz in fact

Last Edited: Fri. Mar 3, 2017 - 05:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

clawson wrote:
EDIT by the way why does the comment say "1MHz"? The Arduino generally runs at 16MHz and 4<<CS10 is surely just 1<<CS12 ? By my maths 16MHz/256 = 62.5kHz in fact

 

It's an ATtiny85 running at 8MHz, and for Timer/Counter1 4<<CS10 gives CK/8.

 

My best guess about what's happening is that after the statement:

 

 TIFR = 1<<OCF1A;

it takes a bit of time for the OCF1A flag to get cleared. The datasheet has the mysterious statement (under TIFR register description):

 

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

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

I believe the OP is referring to the timer running at 1 MHz.  From an 8 MHz clock, that would require /8.  On TIMER1 of the t25/45/85, that is indeed CS1[3:0] = 0b0100:

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

The datasheet has the mysterious statement (under TIFR register description):

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

That is not mysterious.  It is in reference to the use of TIMER1 with the fast peripheral clock, which you are not using.

 

but I would still like to understand why my earlier attempts didn't work.

joeymorin wrote:

If you want to untangle what is actually happening, you'll need to look at the .lss.  Since this is Arduino, there isn't one.  Post the .hex file.  That should be in a temporary folder somewhere, probably named 'build<some-long-string>'.  The folder and it's contents are deleted when closing the Arduino IDE.  Verify or Build the sketch, then go looking for the .hex file and post it here.  Better yet, post the .elf file.

 

You have only 80 clock cycles to play with, so it is likely a race as Lee has suggested.  If you want to know why, you will have to look at the generated code as has been suggested.

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Fri. Mar 3, 2017 - 06:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
You have only 80 clock cycles to play with, so it is likely a race as Lee has suggested.  If you want to know why, you will have to look at the generated code as has been suggested.

 

OK, thank you, I will do that.

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

Just for fun, turn off global interrupts during your loop().  Who knows what other interrupt sources might be enabled.

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

I realise this is months old post, but my input might be of interest to others searching:

Your problem is the Attiny85 and too short a delay time between clearing TOV1 (writing 1 to it) and reading it again.

You need at least 3 system clock cycles after clearing TOV1 before you get the corresponding cleared bit reading.

ie. If TOV1 is initially set and you clear it by writing 1 to it, it will still read 1 if read any less than 3 clock cycles after.

 

From the data sheet:

The read back values are delayed for the Timer/Counter1 (TCNT1) register and flags (OCF1A, OCF1B, and TOV1), because of

the input and output synchronization.

 

Cheers

 

Last Edited: Mon. Jun 26, 2017 - 01:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nope.  OP is not clocking TIMER1 with the fast peripheral clock.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Nope .... Delay is still there irrespective of clock used.  Have a read  12.2 Counter and Compare Units or better still, write some ASM code and test it for yourself.

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

I'll be darned.  The two different delay paths at the bottom of figure 12-2 had not fully caught my attention before now.  Mind you, I've never been bitten by the synchronisation delay on reading back TOV1/OCF1A/B, either.

 

And here I was in post #19 saying the opposite.

 

Good catch.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

I've only just run up against this problem this week. Have never had this problem with other AVRs before, but Tiny85 certainly has it.

Needed a wait of 3 clock cycles. 2 cycles wasn't enough.

Anyway,  this may be of benefit to others.

Cheers