Modified Motor Driver to be Pin Compatible With Panda II

This post is to see if there is any interest in how an Arduino motor driver was modified to be pin compatible with a Panda II. If there is, Ill post the details here or in codeshare - if not Ill save the time and effort. Dont know if a similar approach would also work on the FEZ Cerbuino.

The modified driver is the SainSmart L293D Motor Drive Shield For Arduino Duemilanove Mega (~$14). It can drive 4 DC motors and 2 servos, or two steppers and 2 servos. It was made to work with a Panda by removing the 8 bit shift register IC and directly connecting the Panda II parallel port pins to the empty IC socket. To be pin compatible the code used 2 OutputCompare pins to substitute for PWM pins and also used 2 fez PWM pins that were in the right location. Rotational control used an array of bit masks to toggle the appropriate bits of the parallel port. To better explain this a picture of the modified board is attached.

The motivation for this mod was the inability to locate a controller that was designed to work directly with earlier FEZ offerings such as the Panda II.

Worth documenting here if nothing else !

OK, for what it’s worth here’s the code and a few additional words of explanation. If you are wondering why go to the trouble of pulling the shift register IC on the controller and then directly connecting to the Panda’s parallel port, it greatly simplifies the Panda coding. The original Arduino board did not have as many IO pins available as are on the Panda so the C code sent the setup data in nibbles instead of bytes - it’s just simpler and faster to use the Panda parallel port function.

Much more detail on how the motor controller is designed along with schematics can be found here:

http://www.ladyada.net/make/mshield/

https://docs.google.com/document/pub?id=1OdGS0QHKCdbrbAs_x62gmfSx_UdUPFPgZjwZnVDVHDg

/* ****************************************************************************
 * PII_AFMC (Panda II AdaFruit Motor Controller) This code adapts an L293D Motor Drive Shield 
 * originally designed for an Arduino Duemilanove Mega to be pin compatible with a Panda II. 
 *The shield can drive 4 DC motors and 2 servos, or two steppers and 2 servos, but this
 * version of the code is only for 4 DC motors and is set up to demonstrate operation by
 * cycling though the operation of four motors sequentially (forward - reverse - stop).
 * 
 * Compatibility with a Panda II was achieved by removing the 8 bit shift register IC (74HCT595N)
 * on the motor controllere and directly connecting the Panda II parallel port pins to
 * the empty IC socket. Since the controller also expected 2 PWM outputs where they are not
 * available on the Panda, the code substituted 2 OutputCompare pins for PWM pins. The remaining
 * PWM pins matched existing Panda PWM outputs so they were used as is. Rotational control uses
 * an array of bit masks to toggle the appropriate bits of the parallel port which are connected
 * directly via the empty shift register socket to the L293D dual H-bridge drivers input pins. 
 * 
 * The mask array, "mcbyte[]", is used in the method "sendMcb" with an OR and AND operation to
 * set and clear bits in the variable "mcstatus" - the first of each pair is the OR value and
 * the second is the AND. "mckey" is a number that points to the first paired value in mcbyte and
 * mckey is set by using a descriptive variable name such as m1f (motor 1 forward)
 * to turn motors on or off and select direction.  Motor speed is set by PWM for motors 3&4
 * and by OutputCompare for 1 & 2. Other combined masks could be used
 * for additional and faster control options.
 * 
 ******************************************************************************
 */
using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;

namespace PII_AFMC
{
    public class Program
    {
        public static void Main()
        {
            byte i = 0; // initialize a counter used to pass data to the port
            OutputPort led = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di23, false);  // led used to indicate a write command to the port
            // mcstatus is the control byte that determines the current state of the L293D inputs less the enable inputs which are set via PWM or OoutputCompare
            byte[] mcstatus = new byte[1] { 0 };
            // mcbyte is an array containg the bit mask pairs for the OR and AND operations that set forward, reverse, or off for each motor 
            byte[] mcbyte = new byte[26] {0,0,1,253,2,254,0,252,8,251,4,247,0,243,16,223,32,239,0,207,128,191,64,127,0,63};
            // next line creates recognizeable variables for motor control - not utilized in the demo version
            byte alloff = 0, m1f = 2, m1r = 4, m1o = 6, m2f = 8, m2r = 10, m2o = 12, m3f = 14, m3r = 16, m3o = 18, m4f = 20, m4r = 22, m4o = 24;
            byte mckey;  // set which byte pair to use for a mask
            mckey = alloff;

            // Derfine the PWM used by motors 3 and 4 to set speed
            GHIElectronics.NETMF.Hardware.PWM M3pwm = new PWM(PWM.Pin.PWM5);
            M3pwm.Set(true);
            M3pwm.SetPulse(100000000, 10000000);
            GHIElectronics.NETMF.Hardware.PWM M4pwm = new PWM(PWM.Pin.PWM6);
            M4pwm.Set(true);
            M4pwm.SetPulse(100000000, 10000000);

            // set up the OutputCompare pins (instead of PWMs) used by motors 1 and 2 to set speed 
            uint[] timings = new uint[2];
            uint period = 1000, lowtimem1 = 500, lowtimem2 = 200;
            // When going beyond this demo program this can be extracted to a method that is just called with a motor # and lowtime to set speed
            //use a frequency of 1KHz = period = 1000 us or 1 ms & initially set off time to half the period - later the low time can be changed to control speed
            uint highTime = (uint)(period - lowtimem1);
            timings[0] = highTime;
            timings[1] = 1000 - highTime;
            OutputCompare _ocm1;
            _ocm1 = new OutputCompare((Cpu.Pin)FEZ_Pin.Digital.Di11, false, timings.Length);
            _ocm1.Set(true, timings, 0, 2, true);   //start the waveform//frequency 1KHz
            // Now repeat the process for motor 2
            OutputCompare _ocm2;
            highTime = (uint)(period - lowtimem2);
            timings[0] = highTime;
            timings[1] = 1000 - highTime;
            _ocm2 = new OutputCompare((Cpu.Pin)FEZ_Pin.Digital.Di3, false, timings.Length);
            _ocm2.Set(true, timings, 0, 2, true);   //start the waveform//frequency 1KHz
            // end PWM & OC set up

            // Create an array of digital I/O pins called "pins" that determines which pins are to be used
            // to create the parallel port. "(Cpu.Pin) is a required cast for the "Fez_Pin".  Note 
            // that these specific pins are hardwired to provide faster data updates than other I/O pins.
            Cpu.Pin[] pins;
            pins = new Cpu.Pin[8] {
            (Cpu.Pin)FEZ_Pin.Digital.Di51,
            (Cpu.Pin)FEZ_Pin.Digital.Di50,
            (Cpu.Pin)FEZ_Pin.Digital.Di49,
            (Cpu.Pin)FEZ_Pin.Digital.Di48,
            (Cpu.Pin)FEZ_Pin.Digital.Di47,
            (Cpu.Pin)FEZ_Pin.Digital.Di46,
            (Cpu.Pin)FEZ_Pin.Digital.Di45,
            (Cpu.Pin)FEZ_Pin.Digital.Di44,
            };

            // Create the port called "mtrpins".  GPIO_NONE is used because this port will not be read
            // and is only set up for writing. Again note the cast required for the FEZ_Pin.
            ParallelPort mtrpins;
            mtrpins = new ParallelPort(pins, (Cpu.Pin)FEZ_Pin.Digital.Di24, (Cpu.Pin.GPIO_NONE));

            // NEXT SECTION SEnds control byte to sendMcb method where byte is written to the parallel port. 
            while (true)
            {
                while (i < 26)
                {
                    mckey = i;
                    sendMcb(mtrpins, mcstatus, mcbyte, mckey);
                    Debug.Print("i= " + i.ToString());
                    i = (byte)(i + 2);
                    Thread.Sleep(2000);
                }
               
                // Reset I and start over
                i = 0;
            }

        }
        public static void sendMcb(ParallelPort mtrpins, byte[] mcstatus, byte[] mcbyte, int mckey)
        {
            Debug.Print("mcstatus[0]= " + mcstatus[0].ToString());
            mcstatus[0] = (byte)(mcstatus[0] | mcbyte[mckey]);
            Debug.Print("mcstatus[0] post OR= " + mcstatus[0].ToString());
            mcstatus[0] = (byte)(mcstatus[0] & mcbyte[mckey+1]);
            Debug.Print("mcstatus[0] post AND = " + mcstatus[0].ToString());
            mtrpins.Write( mcstatus, 0 ,1);
        }
    }
}

Thanks for sharing!

Would it make sense to add to the Codeshare library?

I can do if it’s worth it - not sure how much interest there was. Also, I forgot to include some important information on how the pins map. This will save anyone who is interested some time:

pp bit - fez pin - 74HCT595N pin
0 - D51 - 2
1 - D50 - 3
2 - D49 - 4
3 - D48 - 1
4 - D47 - 15
5 - D46 - 6
6 - D45 - 7
7 - D44 - 5