AtMega8 16 bit Fast PWM on OCR1A [solved]

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

Figured out the problem already before I finished typing the thread, but I'll leave it here for reference. See the bottom few lines for what I was doing wrong.

Hi all, just got back into programming some stuff over here and I've run into a bit of a problem.

I'm trying to run a 16 bit fast PWM on OCR1A.

Here is a very simple program that should enable what I want.

#include 

int main()
{
	DDRB |= 0x02;		//Ser OCR1A pin to output
	ICR1 = 65535;		//Set TOP value 
	OCR1A = 30000;		//Set Duty cycle???
	TCCR1A |= 0x82;		//Set COM bits and the first two waveform generator bits
	TCCR1B |= 0x1A;		//Set second two waveform generator bits and prescalar 

	while(1)
	{
		//DDRD |= 0x01;
	}

}

Now, with with waveform generation bits set the way they are, I'm running in Mode 14 on page 98 of the datasheet here.

http://www.atmel.com/images/atme...

What that means is that ICR1 is TOP, otherwise it should act exactly the same as the other fast PWM modes (I've gotten the 8 and 10 bit modes to work, haven't tried any others.)

The problem I'm having is that it's impossible to set the duty cycle. Let's take 8 bit fast PWM for example.

In that mode, you'd use OCR1A to set what is essentially the duty cycle of the square wave (and if it's fast enough, it can be directly related to the measured voltage of that pin by a multimeter). OCR1A really is the "compare value", but if you're reading this you probably figured that out already.

Anyway, Mode 14, the 16 bit fast PWM does not do that. I have the pin attached to an oscilloscope and the pin does turn on, but very, very briefly, and OCR1A has no effect on the duty cycle.

Crap... I just figured it out...

Turns out that using a 16 bit fast pwm is really... really... slow. Helps to do the math sometimes. :lol:

Cranking the Internal RC oscillator up to 8 MHz gave me a 15.5 Hz signal. Not usable of course, but it does make my project quite a bit easier.

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

Quote:
Cranking the Internal RC oscillator up to 8 MHz gave me a 15.5 Hz signal. Not usable of course, but it does make my project quite a bit easier.
In fast pwm mode you control frequency with ICR1 and duty cycle with OCR1A.

With ICR1=65535 you got 15.5 Hz.
With ICR1=6553 you will get 155 Hz.
With ICR1=655 you will get 1550 Hz with 656 possible duty cycle values, which is enough for most purposes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
TCCR1B |= 0x1A;

It'll go 8x faster if you don't use a prescaled clock:

TCCR1B |= 0x19;
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just spent a couple of hours trying to figure out what gives with timer1 mode 14 (AtMega328P). I needed a 440 Hz strobe of certain width and used the following code, which didn't work. The period was correct, but the pulse width not even close (it was much narrower).

  cli();
  DDRB=0x07;         // set OC1A pin output (among others)
  PORTB=0;  
  // Timer 1
  TCNT1=0;           // clear counter
  ICR1=36363;        // 439.9956 Hz from 16 MHz clock
  OCR1A=363;         // 1 % strobe
 
  TCCR1A=0b10000010; // non-inverting, fast PWM
  TCCR1B=0b00011001; // fast PWM, full speed
 
  while(1);

 

Debugging (much, much later, how could I have known...) showed that the value of OCR1A was clipped to 8-bits! Most likely by the changing of mode bits, or the initial (reset) state of them. So moving a single line, and voilà

 

  cli();
  DDRB=0x07;         // set OC1A pin output (among others)
  PORTB=0;  
  // Timer 1
  TCNT1=0;           // clear counter
  ICR1=36363;        // 439.9956 Hz from 16 MHz clock
  
  TCCR1A=0b10000010; // non-inverting, fast PWM
  TCCR1B=0b00011001; // fast PWM, full speed
 
  OCR1A=363;         // 1 % strobe
 
  while(1);

it worked!

 

Browsing the web was unusually unsuccessful on this matter as there are a large number of things that can go wrong and all the solved problems seemed to have output compare values of less than 256, hiding this problem.

 

Note that the code above lacks finesse. One should first change the mode without starting the timer, force strobe the timer and only when everything is in place, start it.

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

That whole scenario is indeed puzzling.

 

What toolchain brand and version are you using?

 

Post the generated code for the "working" and "non-working" versions for inspection.

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

 

This was for Arduino Uno, using the Arduino IDE 1.0.5. I first doubted the tool (while having done >>100 AVR projects, this is something like Arduino project #4 for me), but I wonder if it could possibly be it's fault. Well, let's find out... in that case this should not work:


.include "m328pdef.inc"

 ldi r16,0x07
 out DDRB,r16
 clr r16
 out PORTB,r16

 sts TCNT1H,r16
 sts TCNT1L,r16

 ldi r16,0x8e
 sts ICR1H,r16
 ldi r16,0x0b
 sts ICR1L,r16

 ldi r16,0x01
 sts OCR1AH,r16
 ldi r16,0x6b
 sts OCR1AL,r16

 ldi r16,0x82
 sts TCCR1A,r16

 ldi r16,0x19
 sts TCCR1B,r16

loop:
 rjmp loop

But it does, perfectly. So it indeed is Arduino's fault. What mechanism could possibly result in that kind of failure???

 

The originally posted code was the complete code inside the loop() and there was nothing inside the setup(). Being new to this forum, I'm not certain how to post binary (you did mean the .hex -file?).

Last Edited: Mon. Nov 24, 2014 - 10:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But this is your first mention of Arduino, and you never posted an Arduino sketch!  There is and Arduino forum for Arduino-specific questions.  Did you think dressing up your question with 'AVR' would increase your chances? ;)

 

I'd say it is related to the fact that the Arudino init code has already configured all three timers.  TIMER1 is pre-configured to mode 1 (PWM, Phase Correct, 8-bit).  As such it will only latch the LSB of OCR1x.  Since you updated OCR1A prior to changing timer modes, the MSB of your update is lost.

 

With Arduino, the user is not meant to fiddle with the timer configurations.  You may do so of course, but it's best to know what you're painting over first.

 

It's always safest to clear the timer control registers to their reset-defaults before reconfiguring, in which case it won't matter whether you update OCRnx before changing modes, or after.  Nevertheless, in the case of ICR1, when used as top (modes 8, 10, and 12) that should always written after changing timer modes.  For other modes, ICR1 is read-only.

 

So no, Arduino is not 'at fault'.  It is working exactly as designed and expected.

"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."

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

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

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

 

Last Edited: Mon. Nov 24, 2014 - 01:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:

Did you think dressing up your question with 'AVR' would increase your chances? ;)

 

(Smiley duly noted), No.

 1) I did not know that that would boost 'my chances'. Still not sure...

 2) There was no question to begin with. Quite the opposite, I was sharing a solution. At least that was my intention, to give back something to the community to which I'm deeply in dept.

 3) Unaware of any polarization (AVR vs Arduino), I thought that posting just the actual code would be the correct way. Well, in retrospect, maybe it wasn't. Won't happen again.

 

joeymorin wrote:

I'd say it is related to the fact that the Arudino init code has already configured all three timers.  TIMER1 is pre-configured to mode 1 (PWM, Phase Correct, 8-bit).  As such it will only latch the LSB of OCR1x.  Since you updated OCR1A prior to changing timer modes, the MSB of your update is lost.

 

Now that I know what to search, I find this: "Note that when using fixed TOP values the unused bits are masked to zero when any of the OCR1x Registers are written." You are correct, my bad, should had read the datasheet still more closely.

 

The actual lesson (for me) here is a bit more deeply rooted than just reading the manual better. Earlier I have been relying on the 'disable interrupts and initialize everything' -approach, but this time it was not quite enough as even initialization requires initialization.

 

joeymorin wrote:

So no, Arduino is not 'at fault'.  It is working exactly as designed and expected.

 

I have to admit that you are correct, again.

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

karri wrote:
1) I did not know that that would boost 'my chances'. Still not sure...
It was only meant that the mega/tiny forum sees a great deal more traffic than the relatively new Arduino forum.  In fairness, you tacked onto an existing thread which pre-dates the new site and the new Arduino forum.

 

Quote:
2) There was no question to begin with. Quite the opposite, I was sharing a solution. At least that was my intention, to give back something to the community to which I'm deeply in dept.
It is good that you wanted to share your experiences, and good that you searched for an appropriate thead onto which to tack your post.  I only wonder why you didn't initially disclose the environment.  No harm done.

 

Quote:
3) Unaware of any polarization (AVR vs Arduino), I thought that posting just the actual code would be the correct way. Well, in retrospect, maybe it wasn't. Won't happen again.
I'd say polarisation is perhaps too strong a word.  Arduino does have its scoffers.  I am not among them.  It is just a different colour hammer.  In any event I didn't mean to suggest that a question in the Arduino forum would draw derisive responses, only that such a post would have a smaller audience than one made where you made yours (the mega/tiny forum).  No admonition was intended.

 

 

Quote:
The actual lesson (for me) here is a bit more deeply rooted than just reading the manual better. Earlier I have been relying on the 'disable interrupts and initialize everything' -approach, but this time it was not quite enough as even initialization requires initialization.
I'd suggest the lesson is that when doing something within the Arduino environment (or any other pre-existing code base) which changes the existing configuration, it's valuable to dig deep enough to understand how that environment performs that original configuration.  While that alone might not have answered the question for you, it might have gotten you thinking and eventually land you in the datasheet section you quoted re: masked bits in OCRnx.

 

For the record, the Arduino timer configuration is done in the function init() which is found in the file wiring.c in the folder ./hardware/arduino/cores/arduino/ within your Arduino installation.  There are lots of other source files to peruse in that folder and others.  Don't be afraid to go spelunking ;)

 

In all honesty, I did not intend to squelch your contribution, only to supplement it.  Carry on!

"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."

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

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

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

 

Last Edited: Mon. Nov 24, 2014 - 10:51 PM