[SOLVED] ATmega328P - output PWM on OC0A and OC0B

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

Hi friends.

I need control frequency and duty in PWM. So I set my PWM, but I do not know how to connect both output pins. Any idea, what is wrong?

 

void PWM0_INIT()
{
	/* Set OC0B pin to output */
	DDRD |= (1 << PD5) | (1 << PD6);

	TCCR0A |= (1<<WGM00)|(1<<WGM01)|(1<<COM0B1);
	TCCR0B |= (1<<WGM02)|(1<<CS00)|(1<<CS01);  // dprescaler 32

	OCR0A  = 249;   // Frequncy 1 kHz
	OCR0B  = 125;    // duty 50%
}

 

Or how to control frequency and duty on two output pins? 

Last Edited: Tue. Aug 22, 2017 - 04:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now I have a interrupt, where I generate frequency:

// F_CPU / 2 / Divide / Fcpu
volatile unsigned char setOCR0A = 42; 

ISR(TIMER0_COMPA_vect)
{
	PORTD ^= (1 << PIND5);
}


void PWM0_INIT()
{
	/* Set OC0B pin to output */
	DDRD |= (1 << PIND5) | (1 << PIND6);
	
	// Set the Timer Mode to CTC
	TCCR0A |= (1 << WGM01);
	// Set the value that you want to count to
	OCR0A = setOCR0A;
	TIMSK0 |= (1 << OCIE0A);    //Set the ISR COMPA vect
	TCCR0B |= (1 << CS00) | (1 << CS01);	
	// set prescaler to 64 and start the timer
}

And how to change DUTY CYCLE?

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

Ideally, show a complete test program that demonstrates the issues.  Tell AVR model, clock speed, language, toolchain and version.

 

Tell what you expect to happen.  Tell what is happening.

 

Generally, any single AVR timer, in a PWM mode, will have >>one<< PWM frequency for that timer, depending on the TOP value.  One or more channels, with the same PWM frequency, may have different duty cycles.

 

Tell what frequency you are trying to achieve.  Generally, there is no ISR needed for AVR8 PWM work.

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. Aug 17, 2017 - 07:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Indeed, on a typical AVR8 8-bit timer such as Timer0 on a Mega328, there is no mode to get a variable TOP and have two output channels with different duty cycles.  One must either live with the 8-bit fixed TOP value with only a few frequency choices based on prescaler, or live with one channel on one or more timers.

 

Generally, the 16-bit timer1 has provisions for ICR1 as TOP, and two or three output channels with different duties.

 

If the frequency is slow enough that interrupt servicing time is not onerous, then one could use "soft PWM".  What would you then use for your compare match and "top"?

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

F_CPU = 16MHz
And I need generate frequency from 1Hz to 70Hz. And variable duty cycle from 0% to 100%. 

Last Edited: Fri. Aug 18, 2017 - 05:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I will use TIMER1, where I will set frequency. I will calculate duty cycle in interrupt routine. 

And PWM I will not use.

 

Is it good idea?

Last Edited: Fri. Aug 18, 2017 - 06:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jirkaj4 wrote:
And I need generate frequency from 1Hz to 70Hz.
jirkaj4 wrote:
F_CPU = 16MHz

How do you plan to have an 8-bit timer reach to a one second period?

 

Lessee-- at 16MHz, and /1024 prescaler, that would be 1/16 second reach, right?

 

jirkaj4 wrote:
I will use TIMER1, where I will set frequency. I will calculate duty cycle in interrupt routine. And PWM I will not use. Is it good idea?

If you are using timer1, then why >>not<< use the timer facilities for PWM?

 

Use ICR1 to set TOP for the period.  PB1 and PB2 as outputs.  For 1Hz period and 20% and 40% duty cycles (arbitrary)

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

Plenty of reach.

 

So when you need to change frequency, you calculate the ICR1 value.  Then OCR1A and OCR1B are set to the desired percentage of that for duty.

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

Ok. I calculate  frequency. It is easy. Calculate OCR. But I do not know, hot to calculate duty cycle from minimal to maximal.

F_CPU = 16000000
Divider = 256
F_pwm = require frequency



          F_CPU
ICR1 = ___________________ - 1
        Divider . F_pwm       

Duty: only 50:50

        F_CPU
i = --------------
     2 . Divider


         i
OCR1A = ----
        F_pwm

 

Any idea?

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

I typically do something like:

// Set percent ON time of pwm timer (assumes ICR1_value contains the pwm period of ICR1)
void pwmTimerPercent(uint8_t pwmPercent) {
    // Limit pwmPercent to the range 0 - 100
    pwmPercent =  (pwmPercent > 100 ? 100 : (pwmPercent < 0 ? 0 : pwmPercent));
    OCR1A = (uint16_t)(((uint32_t)pwmPercent * (uint32_t)ICR1_value)/100) ;    // Set pwm percent of pwm period
}

 

David (aka frog_jr)

Last Edited: Tue. Aug 22, 2017 - 03:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. Here is my solution:

uint16_t divider = 256;

void Duty( uint8_t percentage, uint16_t ICR1_value)
{
	percentage =  (percentage > 100 ? 100 : (percentage < 0 ? 0 : percentage));
	uint16_t OCR = (uint16_t)(((uint32_t)percentage * (uint32_t)ICR1_value)/100) ;    // Set pwm percent of pwm period

	OCR1AH = OCR >> 8;
	OCR1AL = OCR & 0xFF;
}

void FrequencyPWM(uint8_t frequency, uint8_t percentage)
{
	uint16_t TOP = F_CPU/(divider*frequency) - 1;
	ICR1H = TOP >> 8;
	ICR1L = TOP & 0xFF;
	Duty(percentage, TOP);
}

 

When you call function:

FrequencyPWM(100, 30);

You set 100Hz and 30% duty. ;)

Last Edited: Tue. Aug 22, 2017 - 04:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Frogger above showed you how to set the pwm duty, as for this:

ICR1H = TOP >> 8;
ICR1L = TOP & 0xFF;

Don't split your register values, the compiler knows how to do that(correctly), so just:

ICR1 = TOP;

Jim

 

 

 

 

 

 

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

And how to calculate duty? Can you show me please?

I thing @frog_jr was pretty clear.  However:

void FrequencyPWM(uint16_t frequency, uint8_t percentage, uint16_t divider)
{
	uint16_t resolution = F_CPU/((uint32_t)divider * frequency);
	ICR1 = resolution - 1;
	OCR1A = (((uint32_t)percentage * resolution) / 100) - 1;
}

 

"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: Tue. Aug 22, 2017 - 04:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Complete PWM1 on ATmega328 is here:

uint16_t divider = 256;

void Duty( uint8_t percentage, uint16_t ICR1_value)
{
	percentage =  (percentage > 100 ? 100 : (percentage < 0 ? 0 : percentage));
	uint16_t OCR = (uint16_t)(((uint32_t)percentage * (uint32_t)ICR1_value)/100) ;    // Set pwm percent of pwm period

	OCR1AH = OCR >> 8;
	OCR1AL = OCR & 0xFF;
}

void FrequencyPWM(uint8_t frequency, uint8_t percentage)
{
	uint16_t TOP = F_CPU/(divider*frequency) - 1;
	ICR1H = TOP >> 8;
	ICR1L = TOP & 0xFF;
	Duty(percentage, TOP);
}

void PWM1_INIT()
{
	DDRB |= (1 << PINB1);
	//DDRB |= (1 << PINB2);
	// Timer/Counter 1 initialization
	// Clock source: System Clock
	// Clock value: 62.500 kHz
	// Mode: Fast PWM top=ICR1
	// OC1A output: Non-Inverted PWM
	// OC1B output: Non-Inverted PWM
	// Noise Canceler: Off
	// Input Capture on Falling Edge
	// Timer Period: 1 s
	// Output Pulse(s):
	// OC1A Period: 1 s Width: 0.2 s
	// OC1B Period: 1 s Width: 0.40001 s
	// Timer1 Overflow Interrupt: Off
	// Input Capture Interrupt: Off
	// Compare A Match Interrupt: Off
	// Compare B Match Interrupt: Off
	TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (1<<WGM11) | (0<<WGM10);
	TCCR1B=(0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (1<<CS12) | (0<<CS11) | (0<<CS10);
	TCNT1H=0x00;
	TCNT1L=0x00;

	FrequencyPWM(50, 10);
}

 

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

joeymorin wrote:
void FrequencyPWM(uint16_t frequency, uint8_t percentage, uint16_t divider) { uint16_t resolution = F_CPU/((uint32_t)divider * frequency); ICR1 = resolution - 1; OCR1A = (((uint32_t)percentage * resolution) / 100) - 1; }

 

Nice. It is fantastic. Thank you.