Search code examples
cavratmega

Control DC Motor Speed with an AVR 8-bit Microcontroller


I'm trying to control DC motor speed. As of right now, to my limited understanding, I suppose I'm able to control the motor with just high and low by setting and clearing bits. The goal is to have the motor run at its 50% or 70% of the max speed, preferrably while gradually increasing and decreasing the speed. Please see the following piece of code and the circuit diagram for the current system configuration.

Here's the motor's datasheet: https://www.ti.com/lit/ds/symlink/drv8801.pdf

The board I'm using is:

ATmega64A Atmel AVR 8-bit Microcontroller

enter image description here

#define MOTOR_CON           PORTA

#define DC_ENABLE_ON        sbi(PORTA, 3)
#define DC_ENABLE_OFF       cbi(PORTA, 3)

#define DC_PHASE_ON         sbi(PORTA, 2)
#define DC_PHASE_OFF        cbi(PORTA, 2)

#define DC_MODE_ON          sbi(PORTA, 1)
#define DC_MODE_OFF         cbi(PORTA, 1)
    
void MotorSpeedSwingUp(void)
{
    DC_ENABLE_ON;
}

void MotorSpeedSwingDn(void)
{
    DC_ENABLE_OFF;
}

void MotorForwardPhase(void)
{
    DC_PHASE_ON;
    DC_MODE_OFF;
}

void MotorReversePhase(void)
{
    DC_PHASE_OFF;
    DC_MODE_OFF;
}

void MotorRun(void)
{
    if(motor_swing_flag == TRUE)
    {
        motor_swing_flag = FALSE;
        MotorSpeedSwingUp();
    }
    else
    {
        motor_swing_flag = TRUE;
        MotorSpeedSwingDn();
    }
}

void MotorBrake(void)
{
    DC_PHASE_OFF;//Forward
    DC_ENABLE_OFF;
    DC_MODE_ON;
}



Many thanks for helping!


Solution

  • The concept of PWM control is to repeat ON and OFF in a very short cycle as shown in Figure 9-3 and so on. The ratio of ON time in the cycle is called "duty cycle" and is proportional to the motor rotation speed as enhzflep comments.

    Let's suppose the cycle is 200usec and we want to have the motor rotate at 70% speed. Then we should repeat the following sequence:

    • Enable the motor.
    • Wait 140usec.
    • Disable the motor.
    • Wait 60usec.

    Assuming your C compiler supports usleep() function, would you please try something like (not tested):

    #define CYCLE 200       // 200usec for 1 cycle
    #include <unistd.h>
    
    /*
     * have the motor run at "duty" speed for "duration" seconds
     * 0.0 <= duty <= 1.0
     */
    void PwmRun(double duty, int duration)
    {
        if (duty < 0.) duty = 0.;                   // lower limit of "duty"
        if (duty > 1.) duty = 1.;                   // upper limit of "duty"
    
        int ontime = (int)(CYCLE * duty);           // PWM on time
        int offtime = (int)(CYCLE * (1. - duty));   // PWM off time
    
        for (int t = 0; t < duration * 1000000; t += CYCLE) {
            if (ontime > 0) DC_ENABLE_ON;
            usleep(ontime);
            if (offtime > 0) DC_ENABLE_OFF;
            usleep(offtime);
        }
    }
    
    // example to run the motor at 70% speed for 3 seconds
    PwmRun(0.7, 3);
    

    It will be easy to modify the code to gradually increase the speed from 50% to 70%.