Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Netduino with Adafruit Motor Shield


  • Please log in to reply
25 replies to this topic

#1 wood

wood

    New Member

  • Members
  • Pip
  • 9 posts

Posted 03 December 2010 - 04:36 PM

Has anyone used the Ada Fruit Motor Shield. I am new to Netduino and need some help getting started with this shield. I would like to control sevos, motors and stepper motors. I need some sample code. I can't seem to convert the C code from the Arduino forums to make this work.

Wood.

#2 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 03 December 2010 - 08:38 PM

Hi Wood, We haven't tested the shield, but it will probably work. Please note that the 32-bit microcontroller on the Netduino has 4 PWM, not 6... You may to write a quick driver for the controller chip on the board... But I don't see many hurdles. If you'd like to post some of your code, perhaps others have played with the shield previously and can assist? Chris

#3 wood

wood

    New Member

  • Members
  • Pip
  • 9 posts

Posted 03 December 2010 - 11:57 PM

Hi Wood,

We haven't tested the shield, but it will probably work. Please note that the 32-bit microcontroller on the Netduino has 4 PWM, not 6... You may to write a quick driver for the controller chip on the board... But I don't see many hurdles.

If you'd like to post some of your code, perhaps others have played with the shield previously and can assist?

Chris



Well that's where the problem is. I'm doing ok with code for just the Netduino, but I don't know where to start to address the ports on the shield. I'm also not sure how to create a driver. Any help would be appreciated.

#4 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 04 December 2010 - 01:03 AM

Hi wood, Let's break it down into pieces. What is the first thing you're trying to do--specifically--that's causing you trouble? And welcome to the Netduino community... Chris

#5 wood

wood

    New Member

  • Members
  • Pip
  • 9 posts

Posted 06 December 2010 - 02:22 PM

When I look at the shield, how do I know which port to refer my program to so I can control it. For example, the Netduino has everthing labeled and the intellisense within MS Visual Studio list the ports for intput and output. On the Netduino I refer to Digital Pin #1 as "Pins.GPIO_PIN_D0" and the switch as "Pins.ONBOARD_SW1". What are the different pins (or connnectors for the motors) called on the Motor Sheild?

Mike

#6 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 06 December 2010 - 06:36 PM

When I look at the shield, how do I know which port to refer my program to so I can control it. For example, the Netduino has everthing labeled and the intellisense within MS Visual Studio list the ports for intput and output. On the Netduino I refer to Digital Pin #1 as "Pins.GPIO_PIN_D0" and the switch as "Pins.ONBOARD_SW1". What are the different pins (or connnectors for the motors) called on the Motor Sheild?


wood, do you have a copy of the shield's schematic? It should have lines going from the digital/analog pins to the motor control h-bridges/terminal.

If you post a link to the schematic here, we'd be happy to walk you through sorting out the connections...

Chris

#7 wood

wood

    New Member

  • Members
  • Pip
  • 9 posts

Posted 06 December 2010 - 06:47 PM

Here's a link to the Schematic. I've been studying this, but obviously need help. I sure do appreciate you assistance..



http://www.ladyada.n...eldv1-schem.png

#8 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 06 December 2010 - 07:11 PM

Here's a link to the Schematic. I've been studying this, but obviously need help. I sure do appreciate you assistance..http://www.ladyada.n...eldv1-schem.png


Header J1 at the top of the schematic represents pins D0-D7. Header J3 represents pins D8-D13 (and GND/AREF).

PWM2A/PWM2B don't exist on the Netduino (at least not yet, although we're working on a "software PWM" to enable these in a future firmware release). PWM0A/0B and PWM1A/1B are really nice 16-bit hardware PWMs on the Netduino, so you should be good to go with those.

So PWM0A/0B drive "stepper motor/driver #2" and PWM1A/1B go to "2 servos on 16-bit pins". These are the connections you should be able to use when using the PWM class features on pins D5/D6 and D9/D10 respectively.

Does that give you a good starting point?

Chris

#9 wood

wood

    New Member

  • Members
  • Pip
  • 9 posts

Posted 06 December 2010 - 07:28 PM

Header J1 at the top of the schematic represents pins D0-D7. Header J3 represents pins D8-D13 (and GND/AREF).

PWM2A/PWM2B don't exist on the Netduino (at least not yet, although we're working on a "software PWM" to enable these in a future firmware release). PWM0A/0B and PWM1A/1B are really nice 16-bit hardware PWMs on the Netduino, so you should be good to go with those.

So PWM0A/0B drive "stepper motor/driver #2" and PWM1A/1B go to "2 servos on 16-bit pins". These are the connections you should be able to use when using the PWM class features on pins D5/D6 and D9/D10 respectively.

Does that give you a good starting point?

Chris


Thank you very much! I will start with that and see what I can do. Again, I really appreciate your help. I will post back my progress, code and picture when I succeed !

Wood

#10 Qwindelzorf

Qwindelzorf

    New Member

  • Members
  • Pip
  • 1 posts

Posted 07 January 2011 - 06:57 AM

Greetings.

I too am attempting to use the Adafruit motor shield with the Netduino. As previously mentioned in this thread, there is not an easy way to control all of the channels on this shield.

I have gone ahead and written up a library for the shield anyways, which is attached. The library is intended to drive DC motors on M3/M4, or one stepper on the pair. Also in the attached zip file is a little test app that attempts to run a DC motor attached to the header marked M4.

As a bit of a disclaimer, this code is basically un-tested. I've looked at it with a logic analyzer, and it seems to be right, but I have not yet tried it with a motor. If you are interested in tinkering with it, please feel free to do so.

Attached Files



#11 wood

wood

    New Member

  • Members
  • Pip
  • 9 posts

Posted 08 January 2011 - 03:06 AM

Thanks for your input. I tired to load it to the Netduino and I am getting a load error. Says it has to be setup to Console App or Class Library for the Output type. I tried both and it still errors. I will keep trying this weekend. If you have any ideas please let me know. I'm kinda new to this.


Greetings.

I too am attempting to use the Adafruit motor shield with the Netduino. As previously mentioned in this thread, there is not an easy way to control all of the channels on this shield.

I have gone ahead and written up a library for the shield anyways, which is attached. The library is intended to drive DC motors on M3/M4, or one stepper on the pair. Also in the attached zip file is a little test app that attempts to run a DC motor attached to the header marked M4.

As a bit of a disclaimer, this code is basically un-tested. I've looked at it with a logic analyzer, and it seems to be right, but I have not yet tried it with a motor. If you are interested in tinkering with it, please feel free to do so.



#12 Jim B

Jim B

    New Member

  • Members
  • Pip
  • 6 posts
  • LocationUSA

Posted 20 January 2011 - 04:43 AM

Count me in as another interested person.

I have the following parts/kits hooked to my Netduino:
Adafruit provides an Arduino driver for the shield:
As mentioned previously, the Netduino only has 4 PWM pins. On the Adafruit MotorShield pins 5 and 6 are connected to M3/M4, while 9 and 10 are connected to the servo headers in the top left.

Oh, and this is the only other thread at this moment mentioning this particular shield:
Here are the problems I am having (with the code provided by Qwindelzorf -- I was able to get it running by copy/pasting into my project):
  • Cannot get both wheels to spin at the same time.
  • Cannot get the wheel to reverse direction once going.
I hope to, in the near future, figure out how to fully control the 2WD platform using this shield. I think that collectively we can get this figured out.

Posted Image

#13 Jim B

Jim B

    New Member

  • Members
  • Pip
  • 6 posts
  • LocationUSA

Posted 21 January 2011 - 03:29 AM

First of all: many, many, many, many, many thanks to Qwindelzorf. Without the code supplied earlier, I would have been dead in the water for much longer than just a day (or two).

Also instrumental:

When attempting to use the ONBOARD_LED to blink on interval, the Netduino threw a CLR_E_PIN_UNAVAILABLE exception. Taking that out also helped uncover that I was "browning out" the Netduino... My solution was to remove the PWR header on the MotorShield and add a 5xAA battery pack to the EXT PWR headers. Keep in mind that there are no protection diodes on the MotorShield.

Here's my code. Simple demo to get the platform spinning in circles, reversing every second.

public static void Main()
{
    DcMotor leftWheel = new DcMotor(MotorHeaders.M4);
    DcMotor rightWheel = new DcMotor(MotorHeaders.M3);
    
    leftWheel.SetSpeed(0);
    rightWheel.SetSpeed(0);
    
    leftWheel.Run(MotorDirection.Forward);
    rightWheel.Run(MotorDirection.Forward);
    
    leftWheel.SetSpeed(90);
    rightWheel.SetSpeed(90);

    while (true)
    {
        leftWheel.Run(MotorDirection.Reverse);
        rightWheel.Run(MotorDirection.Forward);

        Thread.Sleep(1000);

        leftWheel.Run(MotorDirection.Forward);
        rightWheel.Run(MotorDirection.Reverse);
        
        Thread.Sleep(1000);
    }
}

Here are the modified MotorShield and DcMotor classes. The main problem with the original was that the PWM and MotorBits values were static, so when I created another instance of the DcMotor class, it overwrote the values from the previous instance. The MotorBits for 3A/B and 4A/B were also swapped.

No warranty implied.

public sealed class MotorShield
{
    public const Cpu.Pin PWM_0A = Pins.GPIO_PIN_D6; // M4
    public const Cpu.Pin PWM_0B = Pins.GPIO_PIN_D5; // M3

    private static MotorShield _instance = new MotorShield();
    public static MotorShield Instance { get { return _instance; } }

    private OutputPort _motorLatch;
    private OutputPort _motorClock;
    private OutputPort _motorEnable;
    private OutputPort _motorData;

    internal byte LatchState = 0x00;

    private MotorShield()
    {
        _motorLatch = new OutputPort(Pins.GPIO_PIN_D12, false);
        _motorClock = new OutputPort(Pins.GPIO_PIN_D4, false);
        _motorEnable = new OutputPort(Pins.GPIO_PIN_D7, false);
        _motorData = new OutputPort(Pins.GPIO_PIN_D8, false);
    }

    internal void LatchTx()
    {
        //LATCH_PORT &= ~_BV(LATCH);
        _motorLatch.Write(false);

        //SER_PORT &= ~_BV(SER);
        _motorData.Write(false);

        for (int i = 0; i < 8; i++)
        {
            //CLK_PORT &= ~_BV(CLK);
            _motorClock.Write(false);

            int mask = (1 << (7 - i));
            if ((LatchState & mask) != 0)
            {
                //SER_PORT |= _BV(SER);
                _motorData.Write(true);
            }
            else
            {
                //SER_PORT &= ~_BV(SER);
                _motorData.Write(false);
            }
            //CLK_PORT |= _BV(CLK);
            _motorClock.Write(true);
        }
        //LATCH_PORT |= _BV(LATCH);
        _motorLatch.Write(true);
    }
}

public sealed class DcMotor
{
    private PWM _pwm;
    private byte _motorBitA, _motorBitB;

    public DcMotor(MotorHeaders header)
    {
        switch (header)
        {
            case MotorHeaders.M3:
                _motorBitA = (int)MotorBits.Motor3_A;
                _motorBitB = (int)MotorBits.Motor3_B;
                _pwm = new PWM(MotorShield.PWM_0B);
                break;
            case MotorHeaders.M4:
                _motorBitA = (int)MotorBits.Motor4_A;
                _motorBitB = (int)MotorBits.Motor4_B;
                _pwm = new PWM(MotorShield.PWM_0A);
                break;
            default:
                throw new InvalidOperationException("Invalid motor header specified.");
        }

        MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitA) & ~(1 << _motorBitB));
        MotorShield.Instance.LatchTx();

        _pwm.SetPulse(100, 0);
    }

    public void Run(MotorDirection dir)
    {
        switch (dir)
        {
            case MotorDirection.Release:
                MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitA));
                MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitB));
                break;
            case MotorDirection.Forward:
                MotorShield.Instance.LatchState |= (byte)(1 << _motorBitA);
                MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitB));
                break;
            case MotorDirection.Reverse:
                MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitA));
                MotorShield.Instance.LatchState |= (byte)(1 << _motorBitB);
                break;
            default:
                throw new InvalidOperationException("Invalid motor direction specified");
        }

        MotorShield.Instance.LatchTx();
    }

    public void SetSpeed(uint speed)
    {
        if (speed > 100)
        {
            speed = 100;
        }

        _pwm.SetDutyCycle(speed);
    }
}

public enum MotorBits
{
    Motor4_A = 0,
    Motor4_B = 6,
    Motor3_A = 5,
    Motor3_B = 7
}

public enum MotorDirection
{
    Release, Forward, Reverse
}

public enum MotorHeaders
{
    M3, M4
}


#14 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 26 January 2011 - 09:07 PM

First of all: many, many, many, many, many thanks to Qwindelzorf. Without the code supplied earlier, I would have been dead in the water for much longer than just a day (or two).



To both Jim B. and Qwindelzorf, you guys are rock stars! I am incredibly new to this fantastic world of robotics and electronics. To jump from a flashing LED, to servos and DC motors spinning on my Adafruit motor shield, has been one heck of ride. Not only have you sparked my fertile imagination (much to the disapproval of my wife), you have also provided a fantastic tutorial on .Net programming. I have so many things to do now, and so little time - a phrase I hear often throughout these forums.

Have either of your successfully operated a stepper motor with your code? I am having trouble implementing the stepper class with my MOTS1 S28 Unipolar stepper motor. I had originally tired using Pavel Bánský's code found on his website, but had little success. Not understanding the incompatibility between the Adafruit motor shield and Netduino Plus pins, I had the stepper wired to M1 and M2 and was trying to assign the Pins as such:

BipolarStepper motor = new BipolarStepper(new Cpu.Pin[] { 
                            Pins.GPIO_PIN_D4,        // coil A+
                            Pins.GPIO_PIN_D7,        // coil A-
                            Pins.GPIO_PIN_D8,        // coil B+
                            Pins.GPIO_PIN_D12},       // coil B-
                            1.8F);   

The end result was a small incremental rotation of the shaft, but it seemed weak and I was able to turn the shaft manually as well. I assume this had to to do with the incorrect assignment of pins to coils on the stepper.

I've been pouring over the code graciously provided by yourself and Qwindelzorf, and am not entirely sure how I should connect the stepper, and how to instantiate it in my main method.

Any help on this topic would be immensely appreciated.

Once I conquer this task, I also have a 6 coil Unipolar/Bipolar stepper, the PFC55H-48K1, and a touchscreen 3x3 Toshiba T6963C LCD module I want to get working. The Man Cave has never been so busy!

Thanks guys!

#15 ryanUT

ryanUT

    New Member

  • Members
  • Pip
  • 1 posts

Posted 09 February 2011 - 03:50 PM

Hello Secret Labs and Netduino Community, I too am new to the wonderful world of .NET MF development. I am working on a small autonomous robot project and need a good servo driver for this adafruit motor shield. Using the code already given I have been able to get my DC motors working, but I plan on using a servo to rotate a rangefinder, working like a simple sonar. I have checked the project showcase, as previously suggested but was unsuccessful. Have you seen any good servo code to drive this shield? Thanks!

#16 psychohamster

psychohamster

    New Member

  • Members
  • Pip
  • 1 posts

Posted 01 May 2011 - 08:27 PM

To both Jim B. and Qwindelzorf, you guys are rock stars! I am incredibly new to this fantastic world of robotics and electronics. To jump from a flashing LED, to servos and DC motors spinning on my Adafruit motor shield, has been one heck of ride. Not only have you sparked my fertile imagination (much to the disapproval of my wife), you have also provided a fantastic tutorial on .Net programming. I have so many things to do now, and so little time - a phrase I hear often throughout these forums.

I second the kudos to Qwindelzorf and Jim B! Thanks to their generous work, I was able to get a unipolar stepper motor (Sanyo B00324) stepping happily from the Adafruit shield. All I did was combine their work into the following class and include it in my main program.

Gutworks - I've got a bipolar motor sitting around the desk somewhere, I'll see if I can get that working in the next day or so as well and post back the results. (Hooray for Jameco's motor grab bag!)

Note: I was using the ada-recommended external power supply and removed the power jumper from the board (for testing, I just have a 10xAA pack wired to the EXT header).

public sealed class MotorShield
	{
		public const Cpu.Pin PWM_0A = Pins.GPIO_PIN_D6; // M4
		public const Cpu.Pin PWM_0B = Pins.GPIO_PIN_D5; // M3

		private static MotorShield _instance = new MotorShield();
		public static MotorShield Instance { get { return _instance; } }

		private OutputPort _motorLatch;
		private OutputPort _motorClock;
		private OutputPort _motorEnable;
		private OutputPort _motorData;

		internal byte LatchState = 0x00;

		private MotorShield()
		{
			_motorLatch = new OutputPort(Pins.GPIO_PIN_D12, false);
			_motorClock = new OutputPort(Pins.GPIO_PIN_D4, false);
			_motorEnable = new OutputPort(Pins.GPIO_PIN_D7, false);
			_motorData = new OutputPort(Pins.GPIO_PIN_D8, false);
		}

		internal void LatchTx()
		{
			//LATCH_PORT &= ~_BV(LATCH);
			_motorLatch.Write(false);

			//SER_PORT &= ~_BV(SER);
			_motorData.Write(false);

			for (int i = 0; i < 8; i++)
			{
				//CLK_PORT &= ~_BV(CLK);
				_motorClock.Write(false);

				int mask = (1 << (7 - i));
				if ((LatchState & mask) != 0)
				{
					//SER_PORT |= _BV(SER);
					_motorData.Write(true);
				}
				else
				{
					//SER_PORT &= ~_BV(SER);
					_motorData.Write(false);
				}
				//CLK_PORT |= _BV(CLK);
				_motorClock.Write(true);
			}
			//LATCH_PORT |= _BV(LATCH);
			_motorLatch.Write(true);
		}
	}

	public sealed class DcMotor
	{
		private PWM _pwm;
		private byte _motorBitA, _motorBitB;

		public DcMotor(MotorHeaders header)
		{
			switch (header)
			{
				case MotorHeaders.M3:
					_motorBitA = (int)MotorBits.Motor3_A;
					_motorBitB = (int)MotorBits.Motor3_B;
					_pwm = new PWM(MotorShield.PWM_0B);
					break;
				case MotorHeaders.M4:
					_motorBitA = (int)MotorBits.Motor4_A;
					_motorBitB = (int)MotorBits.Motor4_B;
					_pwm = new PWM(MotorShield.PWM_0A);
					break;
				default:
					throw new InvalidOperationException("Invalid motor header specified.");
			}

			MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitA) & ~(1 << _motorBitB));
			MotorShield.Instance.LatchTx();

			_pwm.SetPulse(100, 0);
		}

		public void Run(MotorDirection dir)
		{
			switch (dir)
			{
				case MotorDirection.Release:
					MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitA));
					MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitB));
					break;
				case MotorDirection.Forward:
					MotorShield.Instance.LatchState |= (byte)(1 << _motorBitA);
					MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitB));
					break;
				case MotorDirection.Reverse:
					MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitA));
					MotorShield.Instance.LatchState |= (byte)(1 << _motorBitB);
					break;
				default:
					throw new InvalidOperationException("Invalid motor direction specified");
			}

			MotorShield.Instance.LatchTx();
		}

		public void SetSpeed(uint speed)
		{
			if (speed > 100)
			{
				speed = 100;
			}

			_pwm.SetDutyCycle(speed);
		}
	}

	public sealed class Stepper
	{
		#if MICROSTEPS_8
				private static byte[] microstepCurve = {0, 50, 98, 142, 180, 212, 236, 250, 255};
		#else
				private static byte[] microstepCurve = { 0, 25, 50, 74, 98, 120, 141, 162, 180, 197, 212, 225, 236, 244, 250, 253, 255 };
		#endif

		/// <summary>
		/// Steps per revolution
		/// </summary>
		public readonly uint StepsPerRevolution;

		/// <summary>
		/// The port to which the stepper is connected
		/// </summary>
		public readonly StepperPorts stepperPort;

		private uint usPerStep, stepCounter;
		private uint currentStep = 0;
		private PWM coilA, coilB;
		private uint microsteps;

		public enum StepperPorts
		{
			//M1_M2,
			M3_M4,
		}

		public enum StepType
		{
			/// <summary>
			/// Single Coil activation
			/// </summary>
			Single,
			/// <summary>
			/// Double Coil activation
			/// </summary>
			/// <remarks>Higher torque than Single</remarks>
			Double,
			/// <summary>
			/// Alternating between Single and Double.
			/// </summary>
			/// <remarks>Twice the resolution, but half the speed</remarks>
			Interleave,
			/// <summary>
			/// 
			/// </summary>
			Microstep,
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="stepsPerRev">The number of steps per complete revolution of the output shaft</param>
		/// <param name="port">The port to which the stepper is connected</param>
		public Stepper(uint stepsPerRev, StepperPorts port)
		{
			StepsPerRevolution = stepsPerRev;
			stepperPort = port;
			currentStep = 0;
			microsteps = (uint)microstepCurve.Length;

			int latchState = 0;
			switch (stepperPort)
			{
				/*
				case StepperPorts.M1_M2:
					// Turn off all motor pins
					latchState &= ~(1 << (int)MotorBits.Motor1_A) & 
								  ~(1 << (int)MotorBits.Motor1_B) & 
								  ~(1 << (int)MotorBits.Motor2_A) &
								  ~(1 << (int)MotorBits.Motor2_B);

					coilA = new PWM(PwmPins.pwm2A);
					coilB = new PWM(PwmPins.pwm2B);
					break;
				 */
				case StepperPorts.M3_M4:
					// turn off all motor pins
					latchState &= ~(1 << (int)MotorBits.Motor3_A) &
								  ~(1 << (int)MotorBits.Motor3_B) &
								  ~(1 << (int)MotorBits.Motor4_A) &
								  ~(1 << (int)MotorBits.Motor4_B);

					coilA = new PWM(MotorShield.PWM_0B);
					coilB = new PWM(MotorShield.PWM_0A);
					break;
				default:
					throw new InvalidOperationException("Invalid motor header specified");
			}

			MotorShield.Instance.LatchState = (byte)latchState;
			MotorShield.Instance.LatchTx(); // Enable channels
			coilA.SetPulse(1000000 / 64000, 0); // 64KHz microstep pwm
			coilB.SetPulse(1000000 / 64000, 0); // 64KHz microstep pwm
		}

		/// <summary>
		/// Move the stepper a specific number of steps
		/// </summary>
		/// <param name="steps">How many steps to move</param>
		/// <param name="dir">The direction in which to rotate</param>
		/// <param name="style">The type of stepping to perform</param>
		public void Step(uint steps, MotorDirection dir, StepType style = StepType.Single)
		{
			uint uspers = usPerStep;
			uint ret = 0;

			if (style == StepType.Interleave)
			{
				uspers /= 2;
			}
			else if (style == StepType.Microstep)
			{
				uspers /= microsteps;
				steps *= microsteps;
			}

			while (steps-- > 0)
			{
				ret = OneStep(dir, style);
				Thread.Sleep((int)uspers / 1000); // in ms
				stepCounter += (uspers % 1000);
				if (stepCounter >= 1000)
				{
					Thread.Sleep(1);
					stepCounter -= 1000;
				}
			}

			if (style == StepType.Microstep)
			{
				while ((ret != 0) && (ret != microsteps))
				{
					ret = OneStep(dir, style);
					Thread.Sleep((int)uspers / 1000); // in ms
					stepCounter += (uspers % 1000);
					if (stepCounter >= 1000)
					{
						Thread.Sleep(1);
						stepCounter -= 1000;
					}
				}
			}

		}

		/// <summary>
		/// Sets the stepper speed
		/// </summary>
		/// <param name="rpm">The speed in revolutions per minute</param>
		public void SetSpeed(uint rpm)
		{
			usPerStep = 60000000 / (StepsPerRevolution * rpm);
			stepCounter = 0;
		}

		/// <summary>
		/// Movse the stepper one step
		/// </summary>
		/// <param name="dir">The direction in which to move</param>
		/// <param name="style">The type of stepping to use</param>
		/// <returns>Current step count</returns>
		protected uint OneStep(MotorDirection dir, StepType style = StepType.Single)
		{
			byte a, b, c, d;
			byte ocrb, ocra;

			ocra = ocrb = 255;
			switch (stepperPort)
			{
				/*
				case StepperPorts.M1_M2;
					a = (1<<(int)MotorBits.Motor1_A);
					b = (1<<(int)MotorBits.Motor2_A);
					c = (1<<(int)MotorBits.Motor1_B);
					d = (1<<(int)MotorBits.Motor2_B);
					break;
				*/
				case StepperPorts.M3_M4:
					a = (1 << (int)MotorBits.Motor3_A);
					b = (1 << (int)MotorBits.Motor4_A);
					c = (1 << (int)MotorBits.Motor3_B);
					d = (1 << (int)MotorBits.Motor4_B);
					break;
				default:
					return 0;
			}

			// next determine what sort of stepping procedure we're up to
			if (style == StepType.Single)
			{
				if ((currentStep / (microsteps / 2)) % 2 == 0) // we're at an odd step, weird
				{
					if (dir == MotorDirection.Forward)
					{
						currentStep += microsteps / 2;
					}
					else
					{
						currentStep -= microsteps / 2;
					}
				}
				else // go to the next even step
				{
					if (dir == MotorDirection.Forward)
					{
						currentStep += microsteps;
					}
					else
					{
						currentStep -= microsteps;
					}
				}
			}

			else if (style == StepType.Double)
			{
				if ((currentStep / (microsteps / 2) % 2) != 0) // we're at an even step, weird
				{
					if (dir == MotorDirection.Forward)
					{
						currentStep += microsteps / 2;
					}
					else
					{
						currentStep -= microsteps / 2;
					}
				}
				else  // go to the next odd step
				{
					if (dir == MotorDirection.Forward)
					{
						currentStep += microsteps;
					}
					else
					{
						currentStep -= microsteps;
					}
				}
			}

			else if (style == StepType.Interleave)
			{
				if (dir == MotorDirection.Forward)
				{
					currentStep += microsteps / 2;
				}
				else
				{
					currentStep -= microsteps / 2;
				}
			}

			if (style == StepType.Microstep)
			{
				if (dir == MotorDirection.Forward)
				{
					currentStep++;
				}
				else
				{
					// BACKWARDS
					currentStep--;
				}

				currentStep += microsteps * 4;
				currentStep %= microsteps * 4;

				ocra = ocrb = 0;
				if ((currentStep >= 0) && (currentStep < microsteps))
				{
					ocra = microstepCurve[microsteps - currentStep];
					ocrb = microstepCurve[currentStep];
				}
				else if ((currentStep >= microsteps) && (currentStep < microsteps * 2))
				{
					ocra = microstepCurve[currentStep - microsteps];
					ocrb = microstepCurve[microsteps * 2 - currentStep];
				}
				else if ((currentStep >= microsteps * 2) && (currentStep < microsteps * 3))
				{
					ocra = microstepCurve[microsteps * 3 - currentStep];
					ocrb = microstepCurve[currentStep - microsteps * 2];
				}
				else if ((currentStep >= microsteps * 3) && (currentStep < microsteps * 4))
				{
					ocra = microstepCurve[currentStep - microsteps * 3];
					ocrb = microstepCurve[microsteps * 4 - currentStep];
				}
			}

			currentStep += microsteps * 4;
			currentStep %= microsteps * 4;

			coilA.SetDutyCycle(ocra);
			coilB.SetDutyCycle(ocrb);

			// release all
			MotorShield.Instance.LatchState &= (byte)(~a & ~b & ~c & ~d); // all motor pins to 0

			//Serial.println(step, DEC);
			if (style == StepType.Microstep)
			{
				if ((currentStep >= 0) && (currentStep < microsteps))
					MotorShield.Instance.LatchState |= (byte)(a | B);
				if ((currentStep >= microsteps) && (currentStep < microsteps * 2))
					MotorShield.Instance.LatchState |= (byte)(b | c);
				if ((currentStep >= microsteps * 2) && (currentStep < microsteps * 3))
					MotorShield.Instance.LatchState |= (byte)(c | d);
				if ((currentStep >= microsteps * 3) && (currentStep < microsteps * 4))
					MotorShield.Instance.LatchState |= (byte)(d | a);
			}
			else
			{
				switch (currentStep / (microsteps / 2))
				{
					case 0:
						MotorShield.Instance.LatchState |= (byte)(a); // energize coil 1 only
						break;
					case 1:
						MotorShield.Instance.LatchState |= (byte)(a | B); // energize coil 1+2
						break;
					case 2:
						MotorShield.Instance.LatchState |= (byte)(B); // energize coil 2 only
						break;
					case 3:
						MotorShield.Instance.LatchState |= (byte)(b | c); // energize coil 2+3
						break;
					case 4:
						MotorShield.Instance.LatchState |= (byte)(c); // energize coil 3 only
						break;
					case 5:
						MotorShield.Instance.LatchState |= (byte)(c | d); // energize coil 3+4
						break;
					case 6:
						MotorShield.Instance.LatchState |= (byte)(d); // energize coil 4 only
						break;
					case 7:
						MotorShield.Instance.LatchState |= (byte)(d | a); // energize coil 1+4
						break;
				}
			}

			MotorShield.Instance.LatchTx();
			return currentStep;
		}

		/// <summary>
		/// Releases the motor, allowing it to spin freely
		/// </summary>
		public void Release()
		{
			int latchState = 0;

			switch (stepperPort)
			{
				/*
				case StepperPorts.M1_M2:
					// Turn off all motor pins
					latchState &= ~(1 << (int)MotorBits.Motor1_A) &
								  ~(1 << (int)MotorBits.Motor1_B) &
								  ~(1 << (int)MotorBits.Motor2_A) &
								  ~(1 << (int)MotorBits.Motor2_B);
					break;
				*/
				case StepperPorts.M3_M4:
					// turn off all motor pins
					latchState &= ~(1 << (int)MotorBits.Motor3_A) &
								  ~(1 << (int)MotorBits.Motor3_B) &
								  ~(1 << (int)MotorBits.Motor4_A) &
								  ~(1 << (int)MotorBits.Motor4_B);
					break;
				default:
					throw new InvalidOperationException("Invalid motor header specified");
			}

			MotorShield.Instance.LatchState = (byte)latchState;
			MotorShield.Instance.LatchTx(); // disable channels

			// Ste speed to 0
			coilA.SetDutyCycle(0);
			coilB.SetDutyCycle(0);

		}
	}

	public enum MotorBits
	{
		Motor4_A = 0,
		Motor4_B = 6,
		Motor3_A = 5,
		Motor3_B = 7
	}

	public enum MotorDirection
	{
		Release, Forward, Reverse
	}

	public enum MotorHeaders
	{
		M3, M4
	}


#17 wood

wood

    New Member

  • Members
  • Pip
  • 9 posts

Posted 11 May 2011 - 03:08 PM

First of all: many, many, many, many, many thanks to Qwindelzorf. Without the code supplied earlier, I would have been dead in the water for much longer than just a day (or two).

Also instrumental:


When attempting to use the ONBOARD_LED to blink on interval, the Netduino threw a CLR_E_PIN_UNAVAILABLE exception. Taking that out also helped uncover that I was "browning out" the Netduino... My solution was to remove the PWR header on the MotorShield and add a 5xAA battery pack to the EXT PWR headers. Keep in mind that there are no protection diodes on the MotorShield.

Here's my code. Simple demo to get the platform spinning in circles, reversing every second.

public static void Main()
{
    DcMotor leftWheel = new DcMotor(MotorHeaders.M4);
    DcMotor rightWheel = new DcMotor(MotorHeaders.M3);
    
    leftWheel.SetSpeed(0);
    rightWheel.SetSpeed(0);
    
    leftWheel.Run(MotorDirection.Forward);
    rightWheel.Run(MotorDirection.Forward);
    
    leftWheel.SetSpeed(90);
    rightWheel.SetSpeed(90);

    while (true)
    {
        leftWheel.Run(MotorDirection.Reverse);
        rightWheel.Run(MotorDirection.Forward);

        Thread.Sleep(1000);

        leftWheel.Run(MotorDirection.Forward);
        rightWheel.Run(MotorDirection.Reverse);
        
        Thread.Sleep(1000);
    }
}

Here are the modified MotorShield and DcMotor classes. The main problem with the original was that the PWM and MotorBits values were static, so when I created another instance of the DcMotor class, it overwrote the values from the previous instance. The MotorBits for 3A/B and 4A/B were also swapped.

No warranty implied.

public sealed class MotorShield
{
    public const Cpu.Pin PWM_0A = Pins.GPIO_PIN_D6; // M4
    public const Cpu.Pin PWM_0B = Pins.GPIO_PIN_D5; // M3

    private static MotorShield _instance = new MotorShield();
    public static MotorShield Instance { get { return _instance; } }

    private OutputPort _motorLatch;
    private OutputPort _motorClock;
    private OutputPort _motorEnable;
    private OutputPort _motorData;

    internal byte LatchState = 0x00;

    private MotorShield()
    {
        _motorLatch = new OutputPort(Pins.GPIO_PIN_D12, false);
        _motorClock = new OutputPort(Pins.GPIO_PIN_D4, false);
        _motorEnable = new OutputPort(Pins.GPIO_PIN_D7, false);
        _motorData = new OutputPort(Pins.GPIO_PIN_D8, false);
    }

    internal void LatchTx()
    {
        //LATCH_PORT &= ~_BV(LATCH);
        _motorLatch.Write(false);

        //SER_PORT &= ~_BV(SER);
        _motorData.Write(false);

        for (int i = 0; i < 8; i++)
        {
            //CLK_PORT &= ~_BV(CLK);
            _motorClock.Write(false);

            int mask = (1 << (7 - i));
            if ((LatchState & mask) != 0)
            {
                //SER_PORT |= _BV(SER);
                _motorData.Write(true);
            }
            else
            {
                //SER_PORT &= ~_BV(SER);
                _motorData.Write(false);
            }
            //CLK_PORT |= _BV(CLK);
            _motorClock.Write(true);
        }
        //LATCH_PORT |= _BV(LATCH);
        _motorLatch.Write(true);
    }
}

public sealed class DcMotor
{
    private PWM _pwm;
    private byte _motorBitA, _motorBitB;

    public DcMotor(MotorHeaders header)
    {
        switch (header)
        {
            case MotorHeaders.M3:
                _motorBitA = (int)MotorBits.Motor3_A;
                _motorBitB = (int)MotorBits.Motor3_B;
                _pwm = new PWM(MotorShield.PWM_0B);
                break;
            case MotorHeaders.M4:
                _motorBitA = (int)MotorBits.Motor4_A;
                _motorBitB = (int)MotorBits.Motor4_B;
                _pwm = new PWM(MotorShield.PWM_0A);
                break;
            default:
                throw new InvalidOperationException("Invalid motor header specified.");
        }

        MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitA) & ~(1 << _motorBitB));
        MotorShield.Instance.LatchTx();

        _pwm.SetPulse(100, 0);
    }

    public void Run(MotorDirection dir)
    {
        switch (dir)
        {
            case MotorDirection.Release:
                MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitA));
                MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitB));
                break;
            case MotorDirection.Forward:
                MotorShield.Instance.LatchState |= (byte)(1 << _motorBitA);
                MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitB));
                break;
            case MotorDirection.Reverse:
                MotorShield.Instance.LatchState &= (byte)(~(1 << _motorBitA));
                MotorShield.Instance.LatchState |= (byte)(1 << _motorBitB);
                break;
            default:
                throw new InvalidOperationException("Invalid motor direction specified");
        }

        MotorShield.Instance.LatchTx();
    }

    public void SetSpeed(uint speed)
    {
        if (speed > 100)
        {
            speed = 100;
        }

        _pwm.SetDutyCycle(speed);
    }
}

public enum MotorBits
{
    Motor4_A = 0,
    Motor4_B = 6,
    Motor3_A = 5,
    Motor3_B = 7
}

public enum MotorDirection
{
    Release, Forward, Reverse
}

public enum MotorHeaders
{
    M3, M4
}


How do I control it so it runs continous and change the speed?

#18 Codeblack

Codeblack

    Member

  • Members
  • PipPip
  • 10 posts

Posted 04 August 2011 - 08:09 AM

First of all, thanks guys! This thread has been very helpful getting my motorshield and steppermotors to work. One question though. Could it be that the single and double steptypes are swapped? I'm assuming single means activating a single coil at a time and double means activating two coils at a time. The code does exactly the opposite. Also, I can't get the microstep type of operation to work. The code throws exceptions because the part that determines the pwm duty cycles, is accessing outside the boundaries of the microstepCurve array (index 17, while the array size is 17). I tried to fix the code, but I need to get a decent understanding of microstepping first :unsure: . Has anybody tried this and, maybe, already fixed it? Thanks in advance.

#19 Codeblack

Codeblack

    Member

  • Members
  • PipPip
  • 10 posts

Posted 04 August 2011 - 08:33 AM

Another odd thing I just noticed, is that the interleave steptype does not provide a proper cycle. There are 8 stages in the switch statement that determines which coils to activate (at the end of the OneStep method). Using the interleave steptype, all stages are used. After stage 7, instead of restarting at 0, there is a stage 8. For 8, there is no case statement, so all coils are deactivated. This is also very noticeable in the rotation of the steppermotor, as the rotation isn't smooth and a full 360 degree rotation isn't completed while it should according to the number of steps. Anybody else noticed this?

#20 DaKarch

DaKarch

    New Member

  • Members
  • Pip
  • 3 posts

Posted 06 March 2012 - 06:56 AM

Hey guys,

New to the forum and new to Netduino. So far I am loving it!

I picked up the AdaFruit motor shield and I am having some problems with it. Thought maybe someone here could point me in the right direction.

Hard and software:


Win7 32 Bit
SDK 4.1
.NET xpress C#
NetDuino Plus Rev B
AdaFruit Motor Shield V1.2

Problem:

When the shield is plugged in the PC will not see the ND Plus (also the power led on the shield does not light up). the shield's reset button does reset the ND+.

What I did so far:

A friend has an Arduino. I plugged the shield into there and the power led energized and the Arduino was seen by the PC. Need to test with code on the arduino next.

I have a suspicion...


When I did the getting started project with lighting the onboard LED I had to make the mod:

"If your Netduino’s LED
exhibits the reverse behavior when you run this
sample, you can either update your firmware or
change the code from:
buttonState = button.Read()
to:
buttonState = !button.Read("

So I am wondering if I have to upgrade the firmware to get the shield to work? FYI....I plan on updating the firmware I just don't want to lose my forward momentum with figuring out ND+ and the C#




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.