54 posts / 0 new

## Pages

Author
Message

Guys,

How can I control the RPM of DC motor with PWM on AVR?
Any experiences ?
I use 12V 0.14A DC motor for CPU cooler..

Thanks

Its probably a 'brushless' dc motor that has a couple transitors in it to sequence the poles around. Its only designed to run on one voltage, but might get faster and slower within the operating range.

Imagecraft compiler user

yes it's DC brushless fan, can I use timer1 on PWM mode with ATMEGA128, how can I calculate 2000RPM to frequency in Hertz for PWM ?

thanks

Just test it with an adjustable power supply. How low can you go?

Imagecraft compiler user

There is a spec for pc variable speed fans. This has been discussed before.

Wikipedia gives a good rundown which leads to this:

http://www.formfactors.org/devel...

There's also an atmel appnote on modifying a non speed control fan for variable speed using a tiny13

Just make one thing clear you will use the PWM to make a DC voltages to the motor 5 to 12 V or so. (the speed of the PWM has nothing to do with the speed of the motor).
A PC fan then has a speed output (a pulse for each rotation I think), that you should bring to a input on you AVR an calculate the speed

so for example 16kHz PWM from RPM will get about 2000RPM ?

How about the voltage ? or AVR as a switch for motor driver if I reduce the frequency, the motor is slowing down...

So far I can do, I can use ULN2003, but it's only on and off, I can control the speed (RPM) of the fan...

From I can see, if I increase the voltage manualy, the RPM will go higher...

I define :

```void pwm_init()
{
//f= 16000000/(2*8*256) = 3.8 kHz pre scale 1:8
//pre scale 1:64 = 480Hz
//f= 16000000/(2*64*256) = 480 Hz pre scale 1:64

TCCR1A = _BV(WGM10)  // PWM, Phase Correct, 8-bit
| _BV(COM1A0)   // set OC1A on compare match, clear them at top
| _BV(COM1A1);   // set OC1A on compare match, clear them at top

TCCR1B |= _BV(CS11);
//page 136 ATMEGA128A datasheet

}

void motor_freq(int freq)
{
pwm_init();
OCR1A=freq;

}
```

I will connect OCR1A to my motor...any suggestions ?
thanks

I feed PB5(OC1A) to ULN2003 and it drove the motor, but I don't know what speed do I have there (RPM)

Code :

```void motor_freq(int freq)
{
pwm_init();
OCR1A=freq;

}

int main(void)
{
DDRB  |= 0x20;
while(1)
{
motor_freq(12000);
_delay_ms(2000);
motor_freq(0);

_delay_ms(1000);

motor_freq(16000);
_delay_ms(1000);
motor_freq(0);
_delay_ms(1000);

}
}```

Any formula from Hertz to RPM (motor)?
RPM = Revolution per minute and Hertz revolution per second, 60 RPM = 1Hz,
but I'm not sure, because there's mechanical resistance on the motor....anyone remember ?

Rpm and rps have a direct relationship as you've noted. Why would mechanical resistance alter this?

Its the amount of energy that you put in, not the frequency of the pwm that affects motor speed.

so if I put 12000Hz = 12000/60 RPM = 200RPM,
How can I increase the energy ? change the driver ? and put 12V for the motor ?

What will PWM effect ?

thanks

Quote:
What will PWM effect ?
An interesting question after all this discussion.

PWM is used here for creating a variable voltage.
When you change the duty cycle of PWM then you can see changing voltage on PWM output (OC1A pin) .
The less duty cycle the less voltage.

Then you feed the fan with this voltage.
The less voltage the less RPM.

The frequency of PWM plays a little role here.
More than 20 kHz is choosen, so that we do not hear some whistle from the fan.

(I have simplified things a little, but I hope this can help.)

Try this

```#include
#include

void set_duty(uint16_t percent)
{
OCR1A = (ICR1 * percent) / 100;
}

int main(void)
{
// fast pwm, mode 14, top=ICR1, prescaler 1
TCCR1A = (1<<WGM11)|(1<<COM1A1);
TCCR1B = (1<<WGM12)|(1<<WGM13)|(1<<CS10);

// set f_pwm = 22 kHz
#define prescaler 1
#define f_pwm 22000
ICR1 = (F_CPU / (f_pwm * prescaler)) - 1;

DDRB = (1<<5); // OC1A output

while(1)
{
set_duty(100);   // max fan speed
_delay_ms(3000);

set_duty(80);    // 80%
_delay_ms(3000);

set_duty(60);
_delay_ms(3000);

set_duty(40);
_delay_ms(3000);

set_duty(20);
_delay_ms(3000);
}
}```

the motor is going to rotate but suddenly stop on every "set_duty", why is it ? I must push by hand to make it rotating..

Could you show the schematic?

I use two inputs and two outputs of ULN2003 to drive the motor...
the motor is 12V 0.14A

How does the motor get any power? The ULN is an open-collector driver designed to connect an output to ground.

1. You show no ground connection.
2. Your schematic will simply connect the OUT line to ground so the motor gets no power.

More fundamentally you keep missing the point of using PWM to control the speed. You need to generate a variable DC voltage NOT drive the motor directly with PWM.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

Quote:
you keep missing the point of using PWM to control the speed.

That's what I want to know, what schematic is best doing it ?

Try this

## Attachment(s):

thanks a lot Visovian, is it safe for the MCU ?
what's pin 9 for ? voltage limiter ?

It is quite safe for mcu.

Quote:
what's pin 9 for ?
There are "clamp diodes for switching inductive loads".

but the anode of the diode is not connected to anything ?

is pin 10 connected to pin 12?
thanks

Look at the picture and read the uln2003 datasheet. There's seven outputs and seven diodes. One diode per output with the cathodes common to pin 9

Kartman wrote:
Look at the picture and read the uln2003 datasheet. There's seven outputs and seven diodes. One diode per output with the cathodes common to pin 9

You're really missing the obvious - 7 outputs , seven diodes with the cathode common - the anode for each diode goes to an output. It's clearly shown in the picture and explained in the datasheet.

I have painted the puzzle once again.
I hope now you see the diode connected paralell to the fan (if you do not suffer from daltonism).

## Attachment(s):

done, thanks, what's the different, if I give a higher frequency ? thanks

Quote:
what's the different, if I give a higher frequency ?
You can test it yourself.

Ok thanks,

If I want to change the output to OC1B,

Is this one correct ?

```TCCR1A = (1<<WGM11)|(1<<COM1B1);//channel B
```

It's rotating but the speed changing is not proper..

or ?

```OCR1B = (ICR1 * percent) / 100;

TCCR1A = (1<<WGM11)|(1<<COM1B1)|(1<<COM1B0);//channel B

DDRB = (1<<6); // OC1B output
```

if I use timer1 for CTC and fast PWM on different function, shall I reset TCCR1A and TCCR1B first before I change the configuration ?

I want to use OCR1A for CTC (tone) and OCR1B for PWM (motor) using timer1
thanks

How do you propose to run ctc and pwm modes concurrently?

What do you mean by reset? Should you stop the timer before changing modes? Not required, but it is a good idea to avoid random transitions on the outputs.

Quote:
I want to use OCR1A for CTC (tone) and OCR1B for PWM (motor) using timer1 thanks

1.
A timer can run either in CTC or in PWM mode.
It is impossible that one part (OCR1A) works in CTC mode and another part (OCR1B) in PWM mode.

2.
In fast pwm mode you can use OCRA for tone and OCRB for motor, but the frequency is the same for both.
You cannot set different frequencies for tone and motor.
You can set different duty cycle for tone and motor.

3.
Mega128 has 4 timers.
You can use one timer for tone and another one for motor.

so motor on TCCR2 and OC2 ?....

TCCR2 = (1<<WGM21)|(1<<WGM20)|(1<<CS20)|(1<<COM21);

How can I define f_pwm ?

```void set_duty(uint16_t percent)
{
//OCR1B = (ICR1 * percent) / 100;
OCR2 =  0xFF*percent / 100;
}

void motor_controller()
{    //read page 159 on ATMEGA128 for timer2 control register.
// fast pwm, mode 14, top=ICR1, prescaler 1
//TCCR1A = (1<<WGM11)|(1<<COM1B1);
//TCCR1B = (1<<WGM12)|(1<<WGM13)|(1<<CS10);
TCCR2 = (1<<WGM21)|(1<<WGM20)|(1<<CS20)|(1<<COM21);
// set f_pwm = 22 kHz
#define prescaler 1
#define f_pwm 35000
ICR1 = (F_CPU / (f_pwm * prescaler)) - 1;

//DDRB = (1<<6); // OC1B output
DDRB = (1<<7); // OC2 output

lcd_cmd(LCD_CLEAR);
lcd_string("Motor Speed 100%");

set_duty(100);   // max fan speed
_delay_ms(10000);

lcd_cmd(LCD_CLEAR);
lcd_string("Motor Speed 80%");

set_duty(80);    // 80%
_delay_ms(10000);

lcd_cmd(LCD_CLEAR);
lcd_string("Motor Speed 60%");

set_duty(60);
_delay_ms(10000);

lcd_cmd(LCD_CLEAR);
lcd_string("Motor Speed 40%");

set_duty(40);
_delay_ms(10000);

lcd_cmd(LCD_CLEAR);
lcd_string("Motor Speed 29%");

set_duty(29);
_delay_ms(10000);

lcd_cmd(LCD_CLEAR);
lcd_string("Motor Speed 0%");

set_duty(0);
_delay_ms(5000);

}```

Wouldn't it be wiser to let the more complicated code for motor as it was in timer1 and use timer2 for simplier tone code?
Your can start with using timer2 overflow interrupt every 500 us and toggle the beeper pin in the ISR. You get 1kHz tone.

good idea

the issue is, I connect OC1A to amplifier to get the tone, so I need to use timer2 for motor....?

Quote:
the issue is, I connect OC1A to amplifier to get the tone
And your mother has forbidden to connect it elswhere?

that's correct..hehe

I think the freq is determined only from this :

fOCnPWM=fclk_I/O/N â‹… 256, page 155 atmega128 datasheet, where fclk_I/O = 16 MHz, am I right ?

when I tried from 0% to 100% RPM, the motor didn't start at all, only at 100%, it was starting, any ideas why ?

Quote:
yes it's DC brushless fan
that's why.
Few posts below the cited one have all the details.

You didn't respond when I suggested running the fan on an adjustable bench power supply to determine the range of control. Stupid ol Bob thought that was a good idea. I wish the guys asking for advice would respond to questions for clarification of the problem, and report the results of tests asked for. I betcha that 12V fan will run from about 8V to 16V. Go do the test and come back and tell me I'm an IdGit because it really worked from 10V to 14V.

Imagecraft compiler user

bobgardner wrote:

Stupid ol Bob thought that was a good idea.

You're not the only one Bob.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

How can I drive servo motor like a wiper ?
I've tested this code on AT89S52, it was working,
but not in AVR,

Why is it ?

```while(1)
{
lcd_cmd(0x01);
lcd_string("Servo 100% SPEED");
//motor begin
count=10;
//rotate left every 45 degree begin
for(i=0;i<50;i++)  // 10 equal to 5 degree
{
SERVO_H();
delay(count);
SERVO_L();
_delay_ms(100);
//timer(70);
}
_delay_ms(1000);

//rotate left every 45 degree end
//rotate right every 45 degree begin
for(i=0;i<50;i++)
{
SERVO_L();
_delay_ms(count);
SERVO_H();
_delay_ms(10);
}
_delay_ms(1000);

```

Hi bianchi,

With all respect, wouldn't it be better to learn programming in small steps from tutorials?
Slowly from simple things to more complex.
I am sure everybody here is ready to assist you with it.

I think you yourself must feel that picking codes randomely from internet junkyards is not the right way when you do not understand them.

I don't take from the internet, I make it myself,

This code does the job, only it's not 0 to 180,
suppose to be 0 to 180 degree...why ?

```while(1)
{
//////////////////////////

lcd_cmd(0x01);
lcd_string("Servo -90 degree");
SERVO_H();
_delay_us(900);
SERVO_L();

_delay_ms(20);

SERVO_H();
_delay_us(900);
SERVO_L();

_delay_ms(20);

SERVO_H();
_delay_us(900);
SERVO_L();

//////////////////////
//////////////////////////

//////////////////////

//////////////////////
_delay_ms(1000);
//////////////////////////
lcd_cmd(0x01);
lcd_string("Servo +90 degree");
SERVO_H();
_delay_us(2400);
SERVO_L();

_delay_ms(20);

SERVO_H();
_delay_us(2400);
SERVO_L();

_delay_ms(20);

SERVO_H();
_delay_us(2400);
SERVO_L();

_delay_ms(20);```

Get a scope and look at the output. Is the high part of the frame varying between 1ms and 2ms?

Also note that LCD output could be taking a number of milliseconds. Doing it in the middle of driving the servo may not be the best idea in the world.

Oh and if your code works on an 8051 and not an AVR then scope the outputs and verify that (a) they are being driven (not just hi-Z inputs) and (b) the timing is the same. You might not be setting DDR, your CPU may not be running at the speed you think so _delay_xx() calls will not deliver the requested timing. Or something else might be wrong.

this one what I get :
http://youtu.be/WslfJzpH6-o

code :

```	while(1)
{
//////////////////////////

lcd_cmd(0x01);
lcd_string("Servo -90 degree");
SERVO_H();
_delay_us(900);
SERVO_L();

_delay_ms(15);

SERVO_H();
_delay_us(900);
SERVO_L();

//////////////////////////
lcd_cmd(0x01);
lcd_string("Servo 0 degree");
SERVO_H();
_delay_us(1650);
SERVO_L();

_delay_ms(15);

SERVO_H();
_delay_us(1650);
SERVO_L();

_delay_ms(20);

//////////////////////

//////////////////////
_delay_ms(1000);
//////////////////////////
lcd_cmd(0x01);
lcd_string("Servo +90 degree");
SERVO_H();
_delay_us(2400);
SERVO_L();

_delay_ms(15);

SERVO_H();
_delay_us(2400);
SERVO_L();

_delay_ms(20);

//////////////////////
}//end while
```

Good suggestion, could be because LCD, because I don't use LCD on 51

clawson wrote:
Get a scope and look at the output. Is the high part of the frame varying between 1ms and 2ms?

Also note that LCD output could be taking a number of milliseconds. Doing it in the middle of driving the servo may not be the best idea in the world.

Oh and if your code works on an 8051 and not an AVR then scope the outputs and verify that (a) they are being driven (not just hi-Z inputs) and (b) the timing is the same. You might not be setting DDR, your CPU may not be running at the speed you think so _delay_xx() calls will not deliver the requested timing. Or something else might be wrong.

```#define F_CPU 16000000UL  // 16 MHz