2

I have been for some time on how to control a motor (control its speed) with fast pwm mode with my atmega32. I need to use the 8-bit Timer0, because i have other uses for the other counters. I think I know how to inialise the timer for this task:

void initial_io (void){
    DDRC = 0xFF;
    DDRB = 0xFF;
    PORTA = (1<<PA4)|(1<<PA5);
    TCCR0 = (1<<WGM01)|(1<<WGM00); // PWM mode : Fast PWM.
    TCCR0 = (1<<CS02)|(1<<CS00); // PWM clock = CPU_Clock/1024
}

But then comes the problem. I simply don't know what to do next, what to do on my main.

My exact project is to drive a remote controlled car with acceleration. So when I ask from the car to go forward it must accelerate from stop to maximum speed whith fixed acceleration. I don't know any Assembly, so if you can help me please do it in C. Any help will be much appreciated.

Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
user2149122
  • 171
  • 1
  • 3
  • 14

2 Answers2

6

Well, I guess you're okay with all port B and C pins being outputs. Also, you shouldn't need to do anything in assembly on AVR. The assembly-only stuff is available as macros in avr-libc.

Setup

First off, you're setting up TCCR0 wrong. You have to set all the bits at once, or you have to use a read-modify-write operation (usually TCCR0 |= _BV(bit_num); to set a bit or TCCR0 &= ~_BV(bit_num); to clear it). (_BV(N) is an avr-libc macro that's more legible than the (1<<N) stuff you're using, but does the same thing.) Also, you're missing the polarity of your PWM output, set by the COM00 and COM01 bits. Right now you have them (implicitly) disabled PWM output (OC0 disconnected).

So I'm going to assume you want a positive-going PWM, i.e. larger PWM input values result in larger high-output duty cycles. This means COM01 needs to be set and COM00 needs to be cleared. (See pp. 80-81 of the ATmega32(L) data sheet.) This results in the setup line:

TCCR0 = _BV(WGM01) | _BV(WGM00) // PWM mode: Fast PWM.
      | _BV(COM01)              // PWM polarity: active high
      | _BV(CS02) | _BV(CS00);  // PWM clock: CPU_Clock / 1024

Duty Cycle

Now we get to the actual duty cycle generation. Timer 0 is pretty stupid, and hard wires its BOTTOM to 0 and TOP to 0xFF. This means that each PWM period is PWM_Clock / 256, and since you set PWM_Clock to CPU_Clock / 1024, the period is CPU_Clock / 262144, which is about 33 ms for an 8 MHz CPU clock. So each PWM clock, this counter counts up from 0 to 255, then loops back to 0 and repeats.

The actual PWM is generated by the OC circuit per Table 40. For the COM0* setting we have, it says:

Clear OC0 on compare match, set OC0 at BOTTOM

What this means is that each time the counter counts up, it compares the count value to the OCR0 register, and if they match it drives the OC0 output pin to GND. When the counter wraps around to 0, it drives the pin to VCC.

So to set the duty cycle, you just write a value corresponding to that duty cycle into OCR0:

OCR0 = 0;   // 0% duty cycle: always GND.
OCR0 = 64;  // 25% duty cycle
OCR0 = 128; // 50% duty cycle
OCR0 = 172; // 67% duty cycle
OCR0 = 255; // 100% duty cycle; always VCC. See below.

That last case is there to resolve a common problem with PWM: the number of possible duty cycle settings is always one more than the number of count steps. In this case, there are 256 steps, and if the output could be VCC for 0, 1, 2, … 256 of those steps, that gives 257 options. So rather than prevent either the 0% or 100% case, they make the one-shy-of-100% case disappear. Note 1 on Table 40 says:

A special case occurs when OCR0 equals TOP and COM01 is set. In this case, the compare match is ignored, but the set or clear is done at BOTTOM.

One more thing: if you write to OCR0 in the middle of a PWM cycle, it just waits until the next cycle.

Simulating Acceleration

Now to get the "constant acceleration" you want, you need to have some kind of standard time base. The TOV0 (timer 0 overflow) interrupt might work, or you could use another of the timers or some kind of external reference. You'll use this standard time base to know when to update OCR0.

Constant acceleration just means that the speed changes linearly with time. Taking this a step further, it means that for each update event, you need to change the speed by a constant amount. This is probably nothing more than saturation arithmetic:

#define kAccelStep 4
void accelerate_step() {
    uint8_t x = OCR0;
    if(x < (255 - kAccelStep))
        OCR0 = x + kAccelStep;
    else
        OCR0 = 255;
}

Just do something like this for each time step and you'll get constant acceleration. A similar algorithm can be used for deceleration, and you could even use fancier algorithms to simulate nonlinear functions or to compensate for the fact that the motor does not instantly go to the PWM-specified speed.

Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
  • This was exactly what I was looking for. Thank you very much. One more thing I need to ask. Can you please explain me the meaning of the duty cycle? Also is there a way to make my output Voltage from OC0 at will. I mean if there is any way with an if (...) OC0 = 0? – user2149122 Nov 05 '13 at 08:30
  • 1
    For a square wave with a given period T, duty cyucle is what percentage of T is spent high (for positive duty cycle) or low (for negative duty cycle). So a 75% positive duty cycle waveform with a period of 10 ms will spend 7.5 ms high and 2.5 ms low. As for your second question, I'm not sure what you mean by "make my output Voltage from OC0 at will". 1) The output voltages from OC0 are either VDD or GND. 2) If you want to drive OC0 to be constant VDD or GND, either set the duty cycle to 0% or 100%, or use the FOC0 bit in TCCR0. – Mike DeSimone Nov 05 '13 at 21:10
  • Sorry my mistake. I meant form GND to VDD. So if I want to start from stop and accelerate linearly my OCR0 should be 0, right? – user2149122 Nov 06 '13 at 23:42
  • Initially. Assuming 0 (GND) corresponds to "stop the motor". – Mike DeSimone Nov 07 '13 at 00:19
0

As you seem to be a beginner on AVR programming, I suggest you go the easy way: start with Arduino.

The Arduino environment offers simple functions so you don't need to manipulate the processor registers directly. For instance, to control a PWM output, you simply have to call analogWrite() (documentation here)

Here is a tutorial to hook up a motor to an Arduino.

Here is a tutorial to program a ATMega32 from Arduino IDE

Benoit Blanchon
  • 13,364
  • 4
  • 73
  • 81