ATmega328P Fast PWM w/ variable duty cycle

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

Hi all.

 

I'm using ATmega328P and I'm able to generate desired frequencies using both CTC and Fast PWM (mode 15) on timer 1.  I can also adjust my output duty cycle (D) using CTC, but I have not been able to adjust D using Fast PWM (it is constant at 50%).

 

I am under the understanding that with the following settings my frequency (TOP) will be determined by OCR1A and duty cycle will be determined by OCR1B, but my output does not reflect any changes for D.  I am using SS on the OC1B pin so I don't actually want to output any changes to that pin (maybe this is my problem).

 

Can any of you folks take a look and provide some relevant feedback?  I am trying to output both 10 kHz and 50 kHz frequencies at D = 5% and D = 95% to test the response.  I know my code may not be the most efficient, just doing some quick testing.

 

Thank you!

 

//Using Fast PWM mode with OCRA as frequency selection and OCRB as duty cycle select.
  TCNT1 = 0; //Initialize Timer 1 counter
  DDRB |= (1 << DDB1); //PB1 is an output
  TCCR1A |= (0 << COM1A1) | (1 << COM1A0); //Toggle OC1A on compare match
  TCCR1A |= (0 << COM1B1) | (0 << COM1B0); //Disconnect OC1B
  TCCR1A |= (1 << WGM11) | (1 << WGM10);
  TCCR1B |= (1 << WGM13) | (1 << WGM12); //set CTC mode (WGM13:10 = 0100), or Fast PWM (WGM13:10 = 1111)
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10); //start timer w/ (F_clk/1 = 001), (F_clk/8 = 010) prescaler

  while(1){
  OCR1A = 799; //Frequency select for CTC and Fast PWM.  Output frequency equation above is the same for both.
  OCR1B = 40;  //Duty cycle for Fast PWM ((1 + OCR1B)/(1 + OCR1A)) * 100 = D, disable for CTC
  }

 

This topic has a solution.
Last Edited: Thu. Jun 15, 2017 - 08:06 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

NewMC wrote:
(maybe this is my problem).

That is exactly your situation.  The OC1A output won't be "PWM" when OCR1A is used for TOP.

 

Use mode 14 with ICR1 as TOP, and set up for PWM on OC1A.

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

Thanks for your reply.  I have tried this morning and again just now but I must be implementing it incorrectly.

 

  TCNT1 = 0; //Initialize Timer 1 counter
  DDRB |= (1 << DDB1); //PB1 is an output
  TCCR1A |= (0 << COM1A1) | (1 << COM1A0); //Toggle OC1A on compare match
  TCCR1A |= (0 << COM1B1) | (0 << COM1B0); //Disconnect OC1B
  TCCR1A |= (0 << WGM11) | (0 << WGM10);
  TCCR1B |= (1 << WGM13) | (1 << WGM12); //set CTC mode (WGM13:10 = 0100), or Fast PWM (WGM13:10 = 1100)
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10); //start timer w/ (F_clk/1 = 001), (F_clk/8 = 010) prescaler

  while(1){
  ICR1 = 799;  //This should be my frequency select: F_out = (F_clk/(2*prescaler*(1+ICR1)) = 10 kHz
  OCR1A = 40;  //This should set D to ((1 + OCR1A)/(1 + ICR1)) * 100 = 5.1%

  }

 

Edit: With this I'm getting 10 kHz output but it's still at 50% D.

Last Edited: Thu. Jun 15, 2017 - 06:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

NewMC wrote:
Edit: With this I'm getting 10 kHz output but it's still at 50% D.
Sure, with "toggle on compare match" you will always get 50%.

Stefan Ernst

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

NewMC wrote:
I have tried this morning and again

Side note, from another of my recent replies: http://www.avrfreaks.net/comment...

theusch wrote:

1)  What is with all the |= in your setup?  Surely e.g. ADCSRA = 91<<ADPS2)|(1<<ADIE)|(1<<ADEN)|(1<<ADSC); is smaller and faster and introduces no RMW effects and doesn't rely on any previous value.

 

ADCSRA |= 1<<ADPS2;// PRESCALAR
    ADCSRA |= 1<<ADIE; //INTERRUPT ENABLE
    ADCSRA |= 1<<ADEN; //ADC ENABLE
...
    ADCSRA |= 1<<ADSC; //start conversion
}

 

The same would apply to your timer setup.

 

NewMC wrote:
TCCR1B |= (1 << WGM13) | (1 << WGM12); //set CTC mode (WGM13:10 = 0100), or Fast PWM (WGM13:10 = 1100)

theusch wrote:
Use mode 14 with ICR1 as TOP, and set up for PWM on OC1A.

So, why are you trying to use mode 12?

 

 

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

NewMC wrote:
ICR1 = 799; //This should be my frequency select: F_out = (F_clk/(2*prescaler*(1+ICR1)) = 10 kHz

It would be a lot easier if you'd just tell us what speed your AVR is running at.

 

For 16MHz, for example, to get 10kHz PWM:

 

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 16000.000 kHz
// Mode: Fast PWM top=ICR1
// OC1A output: Non-Inverted PWM
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer Period: 0.01 ms
// Output Pulse(s):
// OC1A Period: 0.01 ms Width: 5.0314 us
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (1<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x9F;
OCR1AH=0x00;
OCR1AL=0x50;
OCR1BH=0x00;
OCR1BL=0x00;

I don't know how you calculated 799, but then again I don't know your F_CPU value.

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

Sorry guys, I got it running in my lab and hadn't had a chance to come back to update you.

 

Sternst was correct also, I initially forgot to change the compare output mode when I made my second post.  Changing it to clear OC1A at BOTTOM made it work, then just had to calculate backwards for frequency and duty cycle adjustment.

 

Yes, I caught that I was looking at the wrong line and set waveform generation for mode 12 instead of 14, so COM1A1:COM1A0 = 11 and WGM13:WGM10 = 1110 did the trick.

 

The bitwise operation method is just what happened to work first, I had initially attempted three different methods on seperate devices (remotely) and this got me there first so I stuck with it.  I'm sure you're correct about it being inefficient.

 

F_cpu = 16 MHz.  I was using CTC mode (with no prescaler) for a previous test and I believe the 799 was a residual value.  799/800 ended up being 50% D for 10 kHz (ICR1 = 1590).

 

This is actually my first project using AVR, my past experience is primarily in PIC micros.

 

Thanks for your great help, this is my first time on this forum and you guys know your stuff!!

Last Edited: Thu. Jun 15, 2017 - 08:05 PM