Thursday, January 17, 2013

DC Motors and an ADF motor controller


My first project needs a motor assembly with high torque so I elected to purchase a Tamiya 72004 Worm Gearbox Kit from Pololu. Additionally I bought a T.I. DRV8833 H-Bridge board for about $7.00. I also bought a Radio Shack bread board and a Tamiya 70157 Universal Plate Set to hold everything together.

As you can see in the photos, I put the motor, a 3 cell AA battery pack and Arduino board (with grove extension) on one side, and another AA battery pack on the other side with the H-Bridge chip on the breadboard. (the assembly is being held by a PanaVise in the photographs).

I assembled the motor gear box for a 336:1 gear ratio but it can also be assembled with a 216:1 gear ratio. The axle is slow, maybe 1 RPS when the DC motor is at 100% so it has very high torque. In fact, if I clamp the drive shaft the motor is able to spin the entire assembly around in a circle with very little strain.

The Seeeduino board is powered by combining both battery packs, 6 rechargeable AA's. Each battery produces 1.2 volts so the total is 7.2 volts. This is slightly below the minimum recommended input voltage for the on board voltage regulator. This voltage, through the power connector, makes it's way to the on-board power regulator. The incoming line has a forward biased diode to keep current from back flowing through the regulator chip. If you have multiple power sources like I do, then check to make sure your regulator is protected. I've read this is recommended so as to not fry your board.

I have to admit that I was skeptical about having the batteries plugged into the Seeeduino board and then just plugging and unplugging the USB connector, but the board switches over with no problems. In fact while it's running with the usb connected it is running on the usb power, not my batteries, but pulling the usb connector causes the board to switch to the batteries with no glitch. Very cool.

Note that I soldered 3 noise suppression diodes to the motor. Pololu sells a noise suppression pack of three 0.1 uF capacitors. You can read more about DC motor noise suppression at the HydraRaptor blog. Note that I did not use ferrite beads in my suppression scheme as specified in the HydraRaptor blog.

Additionally I used a power supply decoupling capacitor based on this tutorial.

here is a diagram of my current project created with Fritzing.


In order to run the motor as I am, you need two PWM outputs. I originally used pins 10 and 11 which are marked on the board as PWM, but one of the pins would not modulate (I can't remember which one). Anyhow, after some fiddling around I move the motor to PWM pins 5 and 6 and it started to work. I did go back and verify that pins 10 and 11 were functioning correctly as digital outputs. So this is a caveat. I've also seen documentation that recommends you connect something like a servo to a specific I/O pin. I don't know why a specific GPIO in a group of similar GPIO's (like the PWM's) is chosen over the others.

I decided to split the power supply, one set to drive the motors and another set added to that to drive the Arduino board. If I decide I need more battery power, I'll add another 3 cell set in parallel to the "lower" set. I did this mainly because I can't drive the motors at 7.2 volts, I don't want to add a voltage regulator to drop 7.2 down to say 6 volts, and I didn't want to stress the onboard 5v regulator. So the motors will be driven at 3*1.2v or 3.6v. (note that a rechargeable 1.5 volt battery produces a constant 1.2 volts until it is fully discharged).

To control the motor I added a new Motor class to my Asynchronous Device Framework. The class is designed to control more DC H-Bridge motors than you could possibly put on an Arduino board, but if I get to 4 motors I'll not have to add any motor class controller code. Once again I use a linked list, in this case virtual motors, that get added at run-time.

There are only three public motor control members, one to start the controller in the ino setup, one to add a motor in the MyController start-up, and one to control it during run-time:

DCMotorControl_Start(uint8_t priority);

DCMotorControl_Add(uint8_t motorID, uint8_t pin1, uint8_t pin2);

DCMotorControl_Set(uint8_t motorID, uint8_t speedPercent,
                                 DCMD_MODE motorMode);

Where DCMD_MODE is defined as

 enum DCMD_MODE
{   
    DCMD_STOP,
    DCMD_COAST,
    DCMD_FORWARD,
    DCMD_REVERSE
};

In my code I define the motor pins, and an arbitrary but unique motor id as:

const uint8_t    DCMOTORin1           =    5;
const uint8_t    DCMOTORin2           =    6;
const uint8_t    DC_SPIN_MOTOR        =    56;

and then create it during runtime startup with:

DCMotorControl_Add(DC_SPIN_MOTOR, DCMOTORin1, DCMOTORin2);

and then control it in the MyController process with a 'make the spin motor go in reverse at 25%' :

DCMotorControl_Set(DC_SPIN_MOTOR, 25, DCMD_REVERSE);

The asynchronous device platform's capabilities far exceed the hardware capabilities of my Seeeduino board. The controller is intended to control a specific type of DC motor controller, the H-Bridge. I could envision a motor type DC_HBRIDGE or DC_STEPPER that would adapt to the controller in a generic way, or you could even create a specific type for a custom hardware controller.

The current ADF motor controller uses the following control logic based on the DRV8833 operation:

// recurse through the list of motor output devices
// and change the state as described in the event args
void DCMCListWalker(VMODevice * link, DCMDEventArg const * device)
{
    if(link != NULL)
    {
        if( link->m_MotorID == device->motorID )
        {
            uint8_t speed = map(device->motorSpeedPercent,0,100,0,255);
            if(device->motorMode == DCMD_COAST )
            {
                analogWrite(link->m_Pin1, 0);
                analogWrite(link->m_Pin2, 0);
            }
            else if(device->motorMode == DCMD_FORWARD )
            {
                analogWrite(link->m_Pin1, speed);
                analogWrite(link->m_Pin2, 0);
            }
            else if(device->motorMode == DCMD_REVERSE )
            {
                analogWrite(link->m_Pin1, 0);
                analogWrite(link->m_Pin2, speed);
            }
            else // STOP
            {
                analogWrite(link->m_Pin1, 255);
                analogWrite(link->m_Pin2, 255);
            }
        }
        else if(link->m_NextVMODevice != NULL )  // not it, go deeper
            DCMCListWalker(link->m_NextVMODevice, device);
    }
}

Also note that although I use forward and reverse, it would probably be good to add something like clockwise and counter-clockwise, or left and right to the enumerations for readability.

No comments:

Post a Comment