DC Motor Speed Control

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

Hi

 

I work on dc motor speed control with PWM technique. I use 1k pot for speed control. I found a sample code and I try to understand it. Let me share you code. I figured out how to code works but I don't understand whay the last line of the code divided the value read from ADC by 4

#define F_CPU 8000000UL			/* Define CPU Frequency 8MHz */
#include <avr/io.h>			/* Include AVR std. library file */
#include <avr/interrupt.h>
#include <stdio.h>			/* Include std. library file */
#include <util/delay.h>			/* Include Delay header file */

volatile uint8_t Direction = 0; 

void ADC_Init()				/* ADC Initialization function */
{
	DDRA = 0x00;			/* Make ADC port as input */
	ADCSRA = 0x87;			/* Enable ADC, with freq/128 */
	ADMUX = 0x40;			/* Vref: Avcc, ADC channel: 0 */
}

int ADC_Read(char channel)		/* ADC Read function */
{
	ADMUX = 0x40 | (channel & 0x07);/* set input channel to read */
	ADCSRA |= (1<<ADSC);		/* Start ADC conversion */
	while (!(ADCSRA & (1<<ADIF)));	/* Wait until end of conversion */
	ADCSRA |= (1<<ADIF);		/* Clear interrupt flag */
	_delay_ms(1);			/* Wait a little bit */
	return ADCW;			/* Return ADC word */
}

ISR(INT0_vect)
{
	Direction = ~Direction;		/* Toggle Direction */
	_delay_ms(50);			/* Software de-bouncing control delay */
}

int main(void)
{
	DDRC = 0xFF;			/* Make PORTC as output Port */
	DDRD &= ~(1<<PD2);		/* Make INT0 pin as Input */
	DDRB |= (1<<PB3);		/* Make OC0 pin as Output */
	GICR = (1<<INT0);		/* Enable INT0*/
	MCUCR = ((1<<ISC00)|(1<<ISC01));/* Trigger INT0 on Rising Edge triggered */	
	sei();				/* Enable Global Interrupt */
	ADC_Init();			/* Initialize ADC */
	TCNT0 = 0;			/* Set timer0 count zero */
	TCCR0 = (1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00)|(1<<CS01);/* Set Fast PWM with Fosc/64 Timer0 clock */
	while(1)
	{
		if (Direction !=0)	/* Rotate DC motor Clockwise */
			PORTC = 1;
		else			/* Else rotate DC motor Anticlockwise */
			PORTC = 2;
		OCR0 = (ADC_Read(0)/4);	/* Read ADC and map it into 0-255 to write in OCR0 register */
	}
}

 

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

What range of values does adc_read return,? Might it return values from 0 to 1023? In which case those would need changing to 0 to 255 to fit with the PWM chosen.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "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." - Heater's ex-boss

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

0 to 255. Can I ask something. Timer0 is used in the code that I shared. I mean  an 8 bit timer is used because of that is this range 0 to 1023

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

You can get rid of ADC_init, since most of it is done anyhow in the ADC_Read....just put the DDRA with the other DDR settings

 

these are improper:

DDRD &= ~(1<<PD2);		/* Make INT0 pin as Input */
	DDRB |= (1<<PB3);		/* Make OC0 pin as Output */

you should be using =     

    DDRD= what you want it set to

    DDRB= what you want it set to

 

You did it for DDRC...what happened to the others???

 

BE EXPLICIT...don't assume other bits are already set to blah or blahh...do it here so you can see what is going on

 

I believe you should generally set  

TCCR0 =

BEFORE the sei...set up the timer exactly the way you want , THEN turn on the IRQ's

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Fri. Nov 8, 2019 - 07:14 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

atahan wrote:
speed control with PWM technique.

Note that PWM just varies the power delivered to the motor - it does not control speed.

 

If nothing else changes, more power will give higher speed - but it will not control the speed to keep it steady as the load on the motor varies ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 use 1k pot for speed control.

 

Note that PWM just varies the power delivered to the motor - it does not control speed.

 

If nothing else changes, more power will give higher speed - but it will not control the speed to keep it steady as the load on the motor varies ...

To your point it is not a closed loop control system...fixed PWM does not equate to a fixed speed  

I suppose OP is correct...he can certainly control (vary) the speed by twiddling the pot  (he becomes the master cyborg motor automaton)...OP doesn't say anything about regulating or maintaining the speed.

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

avrcandies wrote:
OP doesn't say anything about regulating or maintaining the speed

Indeed.

 

But some people may take "control" to mean "regulate or maintain" - so just making clear that PWM alone will not do that.

 

As you say, something else would need to "close the loop".

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank u for all comment. I wonder why divided ADC value by 4. Can you explain the last line of the code block? 

OCR0 = (ADC_Read(0)/4);

 

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

Post #2 explained it already. Adc is 0..1023, pwm is 0..255. 1023 /4 is ........?

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

ADC reads 10 bits so is a number between 0 and 1023. The OCR0 register is 8 bit. It can only hold 0..255. 1024/4 is 256.

 

The same line could have been written:

OCR0 = ADC_Read(0) >> 2;

(in fact that is almost certainly what the C compiler will generate anyway as it will spot that "/4" is special - just as any binary division is).

 

>>2 perhaps conveys more accurately what is happening here. A 10 bit reading is being moved 2 places to the right to reduce it to 8 bits with the lowest 2 bits being lost.

 

(BTW Brian did explain this already in #2)

 

EDIT: PS forgot to say that this requirement to have just 8 of 10 bits from the ADC is such a common request that Atmel designed a feature into the ADC. While readings are always 10 bits and returned in ADCH and ADCL, if you are only interested in 8 of those bits you can use the ADLAR bit in the ADMUX register which says put the 10 bits in the upper 10 of the 16 bits formed by ADCH/ADCL rather than the usual lower 10 of 16 (which means that a reading is really the lower 2 bits in ADCH and the 8 bits in ADCL). If you ADLAR (Left Adjust Result) then the 8 most significant bits are in ADCH and the lowest 2 bits are the upper 2 in ADCL - so if you just read ADCH and ignore ADCL you get the 8 most significant bits anyway without needing to do any /4 or >>2 operation)

Last Edited: Fri. Nov 8, 2019 - 11:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sorry about that, thank you so much. 

Can I ask one more question If I use 16 bit timer, do I still need to divede the ADC value by 4?

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

So if you wanted to use the 8 bit thing you could change ADC_read() to be:

uint8_t ADC_Read(uint8_t channel)		/* ADC Read function */
{
	ADMUX = 0x40 | (1 << ADLAR) | (channel & 0x07);/* set input channel to read */
	ADCSRA |= (1<<ADSC);		/* Start ADC conversion */
	while ((ADCSRA & (1 << ADSC)));	/* Wait until end of conversion */
	return ADCH;			/* Return ADC word */
}

then you would just use:

		OCR0 = ADC_Read(0);	/* Read ADC and map it into 0-255 to write in OCR0 register */

which looks cleaner all round.

 

BTW i changed the way the reading is done - there's no point messing with ADIF when ADSC can be used and it is self-clearing.

Last Edited: Fri. Nov 8, 2019 - 11:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

atahan wrote:
Can I ask one more question If I use 16 bit timer, do I still need to divede the ADC value by 4?
If you use a 16 bit then you can use all 10 bits of the ADC reading to set the duty more accurately. But you may want to use a 10 bit mode (or a mode where TOP is defined to be 10 bits).

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

awneil wrote:
Note that PWM just varies the power delivered to the motor - it does not control speed.

Actually, PWM will vary the voltage applied to the motor.  The power will also depend upon the mechanical load on the motor.

Letting the smoke out since 1978

 

 

 

 

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

Hmm ... you got me thinking there!

 

The power into the motor is limited by the motor's back-emf; as the motor slows, back-emf drops, so power will increase.

So there's some self-regulation there.

 

With PWM, you are always applying the full voltage during the ON period - so potentially full power.

 

So it seems that even using plain PWM (with no control loop) would give better speed regulation than an analogue variable voltage ... ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So it seems that even using plain PWM (with no control loop) would give better speed regulation than an analogue variable voltage ... ?

Doubtful...remember during the off time,  the motor is coasting 

 

 

The power into the motor is limited by the motor's back-emf; as the motor slows, back-emf drops, so power will increase.

So there's some self-regulation there.

Well sorta true, but remember the reason for the motor to slow (and draw more power) is  a heavy mechanical load is drawing power from the motor & causing speed to droop. 

 

The back emf does slow (regulate) the motor since it lowers the effective applied voltage, so it runs at a certain speed given a certain supply voltage & certain load.  However, I don't think it regulates, per se, vs load change.

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!