Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top