• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PWM problem: can't set duty cycle

Status
Not open for further replies.

mikewax

Member
Hello
can someone look at my code and figure out why the duty cycle parameter isn't working here? i'm using timer1 on the Atmega 328 with code i copied from the net https://sites.google.com/site/qeewiki/books/avr-guide/pwm-on-the-atmega328

C code:
// BLINK the yellow (line D9) LED with period=2s, brightness grows and cycles every 8 periods
#include <avr/io.h>

int x = 0;
void setup() {
// setup fast PWM, timer1 rolls over every 32 units
ICR1 = 32;
TCCR1A |= (1 << COM1A1)|(1 << WGM11);
TCCR1B |= (1 << WGM12)|(1 << WGM13);
}

void loop() {

x += 4;
OCR1A = x%32; // Duty cycle = (x mod 32)/32
DDRB |= (1 << DDB1); // set pin D9 to output
TCCR1B |= (1 << CS10); // enable timer1
delay(1000);

DDRB &= ~(1 << DDB1); // D9 = input
TCCR1B &= ~(1 << CS10); // disble timer1
delay(1000);
}

when i load and run the program, the LED goes full on, full off, 100%, 0%, with no gradation in between. OCR1A is supposed to be the duty cycle to fade the LED up and down, but it doesn't.
 

wkrug

Active Member
Hi Mike,

try this:
/*
* TimerTest.c
*
* Created: 08.01.2017 23:30:00
* Author : USER
*/
#define F_CPU 8000000
#include <avr/io.h>
#include <util/delay.h>


int main(void)
{
int x = 0;
// setup fast PWM, timer1 rolls over every 32 units
ICR1 = 32;
TCCR1A |= (1 << COM1A1)|(1 << WGM11);
TCCR1B |= (1 << WGM12)|(1 << WGM13);





while (1)
{
x += 4;
OCR1A = x%32; // Duty cycle = (x mod 32)/32
DDRB |= (1 << DDB1); // set pin D9 to output
TCCR1B |= (1 << CS10); // enable timer1
_delay_ms(1000);

//DDRB &= ~(1 << DDB1); // D9 = input
//TCCR1B &= ~(1 << CS10); // disble timer1
//_delay_ms(1000);
}
return 0;
}
The LED should go brighter every second until the max level is reached.
Than is switched off and the cycle starts again.
When You wanna to switch off the LED i would write a 0 into the OCR1A register.
So the
DDRB |= (1 << DDB1); // set pin D9 to output
TCCR1B |= (1 << CS10); // enable timer1
instuction is only once to do while initialisation.
 

DerStrom8

Super Moderator
Why are you changing the direction of D9 mid-program?
 

wkrug

Active Member
Why are you changing the direction of D9 mid-program?
I'm not shure.
But when i rememer the right way, the PWM never swich off completely in FAST PWM Mode.
In spite of OCR1A ist set to 0, the OCR Output Pin in switched on for 1 clock cycle.
 

DerStrom8

Super Moderator
I'm not shure.
But when i rememer the right way, the PWM never swich off completely in FAST PWM Mode.
In spite of OCR1A ist set to 0, the OCR Output Pin in switched on for 1 clock cycle.
Sorry, just to be clear I was talking to the OP. I think you suggested only setting the direction during the initialization.
 

wkrug

Active Member
Sorry, just to be clear I was talking to the OP. I think you suggested only setting the direction during the initialization.
Yes, but there are a few problems in programm structure too.
I've simulated it with AVR Studio 7.
In fast PWM mode the OC1A Pin is high for one clock cycle, while OCR1A is set to 0.
That's the cause why I would not use fast pwm in a solution when possible.
If that peak is a Problem you have to switch off the pin, when set OCR1A to 0.

When the Controller runs with 8MHz clock, the PWM has a frequency of 250 000kHz.
Only to reduce flicker, that makes no sense.
Additional You have only 32 dimm steps with a 16Bit Counter.

In this solution the LED only should to be dimmed up and down.
Possibly it's no problem if the LED is'n completly off.

My suggestion is to count a variable up and down and write it into OCR1A.
So the Timer 1 wouldn't be disturbed while his operation.

e.g.
uint8_t i;
...
...
for(i=0,i<32,i++) //dimms up in 32 second's
{
OCR1A=i;
_delay_ms(1000);
}
for(i=31,i<32,i--) //dimms down until i is overflown =255
{
OCR1A=i;
_delay_ms(1000);
}

So You never have to change timer parameters in main loop.
The proper way should be to implement the dimming routine into a timer interrupt, because the controller wasn't blocked only for waiting.
But that's dependent of the application the OP want to create.
 

mikewax

Member
Yes, but there are a few problems in programm structure too.
I've simulated it with AVR Studio 7.
In fast PWM mode the OC1A Pin is high for one clock cycle, while OCR1A is set to 0.
That's the cause why I would not use fast pwm in a solution when possible.
If that peak is a Problem you have to switch off the pin, when set OCR1A to 0.
thanx for the response i had long given up on arduino altogether and switched to a whole nother environment.
But you had the answer alright, the delay function was knockin out timer 1. the problem remains, though, because when i replaced delay with _delay_ms it knocked out timer2.
i need to use both timers in fast PWM mode because my circuit has 2 seperate switchmode convertors. and yeah it seems you gotta set the pin to input if you wanna really turn it off. The "spike" problem in these timers is a real drag.
but maybe if i rearrange my pins and use timer0 it could work. this whole nightmare happened in the first place because some weenie document said that i couldn't use delay and timer0 at the same time.
now i dunno what to believe. either way i had to give up on the atmega anyway because i found another chip, the STM32f, that's significantly cheaper.
BTW does that Studio 7 software actually simulate the atmega chip in operation? and what does it cost?
 

wkrug

Active Member
AVR Studio 7 is free to download from the ATMEL Page.
But You must type in Your E-Mail adress to get the Download link.
Included is the AVR GCC Compiler and the ASSEMBLER compiler from studio 4.
Studio 7 can simulate all the functions of the choosen chip with breakpoints, memory maps, single step mode and so on.
But there are a few errors in simulation at some chips and some older one wouldn't not further supported.

You also can use JTAG to debug the real hardware with studio 7.
JTAG only works at Controllers above ATMEGA 16, JTAG interfaces are not cheap and use some Controllerports exclusive for JTAG.

For simulation You also can create a stimili file to simulate external input signals - I never used that, because You can edit all values manual while simulation is paused.

The problem with the delay function is not a problem of the AVR chips, but a problem of the compiler.
In GCC are used programm loops to generate the delay and not any hardware.
The disadvantage is, the controller main programm is blocked while the delay loop is running.
 

mikewax

Member
The problem with the delay function is not a problem of the AVR chips, but a problem of the compiler. In GCC are used programm loops to generate the delay and not any hardware.
The disadvantage is, the controller main programm is blocked while the delay loop is running.
i don't understand. when i used the delay, Timer1 stopped working. And when i used _delay_ms, Timer2 stopped working. So it appears that these functions are reading and writing to the TCNT1 & TCNT2 registers, right?
 

wkrug

Active Member
i don't understand. when i used the delay, Timer1 stopped working. And when i used _delay_ms, Timer2 stopped working. So it appears that these functions are reading and writing to the TCNT1 & TCNT2 registers, right?
"delay" and "_delay_ms" are functions of the compiler.
Normally these functions are programm code in the delay.h file You must include first.
What that does really is a thing of the compiler.
You can use many loop's there so the controller takes many cycles to compute that, or You'll use a timer to get a delay.
When You'll be shure what the controller have to do, You can create Your own wait cycles.
e.g. You can count up a variable in the TIMER OVERFLOW interrupt.
So You get fixed time interval's and You can use the timer You want to have.
But with a prescaler 0f 1 and only 32 processor cycles the controller only will hang in this interrupt most time.
 

mikewax

Member
oh i think i see what your saying. a 16MHz clock and a 32 unit counter means that the overflow interrupt will execute every 2us and serve as a time base. i never woulda thoughta that.
so if i go:
ISR(TIMERx_OVF_vect){ y--;}
and i do:
y=whatever;
while (y);

then i get a delay of whatever*2us and no interference with TIMERx. that's a perfect solution.
but is there a conflict of scope? like does y have to be a global or a volatile?
 

wkrug

Active Member
You should take a volatile variable on the top of the main routine, behind the includes.

#include....
....
volatile uint16_t y=0;

.....
ISR(.....
.....
....
uint8 main(void)
{.....
......
}
 

mikewax

Member
You should take a volatile variable on the top of the main routine, behind the includes.
#include....
....
volatile uint16_t y=0;
.....
ISR(.....
.....
....
uint8 main(void)
{.....
......
}
gotcha that IS a damn fine idea. concurrent and atomic. that's what i'll be doing from now on. DANKE SHOEN :)
 
Status
Not open for further replies.

Latest threads

EE World Online Articles

Loading
Top