Multiplexed unipolar stepper motor control

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 

Multiplexed unipolar stepper motor control

2,616 次查看
woohoo
Contributor I

Hi

 

I am a newcomer to microcontrollers and have been struggling with the coding for the control of 6 UAG2 unipolar stepper motors that are multiplexed through a 74LS151N using the GT16A  microcontroller. I am trying to recive the motor instruction through SCI and in that data it identifies which motor needs to be selected and in which direction it should rotate for x amount of steps. I don't think I am going about this in the correct manner and am desperately in need of help especially since I am guessing I need to use the TPM module

 

The wiring diagram is attached and the code is below:

 

The SCI interrupt to receive the data:

 

interrupt 20void SCI_interrupt (void) {    asm ("nop");    if (SCI2S1_RDRF) {            // SCI 2 receive interrupt        if (PacketReceiveIndex == 0 && SCI2D == 0x23      // ascii #                                  || SCI2D == 0x41    // ascii A                                  || SCI2D == 0x42    // ascii B                                  || SCI2D == 0x43    // ascii C                                  || SCI2D == 0x44    // ascii D                                  || SCI2D == 0x45    // ascii E                                  || SCI2D == 0x46    // ascii F                                  || SCI2D == 0x61    // ascii a                                  || SCI2D == 0x62    // ascii b                                  || SCI2D == 0x63    // ascii c                                  || SCI2D == 0x64    // ascii d                                  || SCI2D == 0x65    // ascii e                                  || SCI2D == 0x66) { // ascii f            StartReceived = 1;      }        else if (StartReceived == 1 && SCI2D == 0x23) {   // ascii #       PacketReceive[PacketReceiveIndex] = SCI2D;          if (PacketReceiveIndex == 15) {         // done??              StartReceived = 0;                    // reset for next        PacketReceiveIndex = 0;               // reset for next        SCI2C2_TIE = 0;                PiezoMove (PacketReceive);                                                }             else PacketReceiveIndex++;              // not done, increment index        }         else if (StartReceived == 1 && SCI2D == 0x41      // ascii A                                  || SCI2D == 0x42    // ascii B                                  || SCI2D == 0x43    // ascii C                                  || SCI2D == 0x44    // ascii D                                  || SCI2D == 0x45    // ascii E                                  || SCI2D == 0x46    // ascii F                                  || SCI2D == 0x61    // ascii a                                  || SCI2D == 0x62    // ascii b                                  || SCI2D == 0x63    // ascii c                                  || SCI2D == 0x64    // ascii d                                  || SCI2D == 0x65    // ascii e                                  || SCI2D == 0x66) { // ascii f                                        PacketReceive[PacketReceiveIndex] = SCI2D;          if (PacketReceiveIndex == 15) {         // done??              StartReceived = 0;                    // reset for next        PacketReceiveIndex = 0;               // reset for next        SCI2C2_TIE = 0;      }    }  }       }

 and the actual motor control code:

void motorStep (unsigned char Data[]) {  int loopCount = 0;  int i = 0;  unsigned char motorData[4];  motorData[0] = 0x09;  motorData[1] = 0x0A;    motorData[2] = 0x06;  motorData[3] = 0x05;    if (Data[0-7] == 0x41) {     // motor 1 forward: ascii A        // select motor with multiplexer    PTBD = 0x00;                           // Send 0,0,0 to mux        for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i++];                       loopCount++;      asm ("nop");            if (i == 3) i = 0;           }     }    else if (Data[0-7] == 0x61) {     // motor 1 backward: ascii a      // select motor with multiplexer    PTBD = 0x00;                     // Send 0,0,0 to mux          for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i--];      loopCount++;      asm ("nop");            if (i == 0) i = 3;              }  }       else if (Data[0-7] == 0x42) {     // motor 2 forward: ascii B    // select motor with multiplexer    PTBD = 0x01;                           // Send 0,0,1 to mux        for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i++];                       loopCount++;      asm ("nop");            if (i == 3) i = 0;           }     }    else if (Data[0-7] == 0x62) {     // motor 2 backward: ascii b      // select motor with multiplexer    PTBD = 0x01;                     // Send 0,0,1 to mux          for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i--];      loopCount++;      asm ("nop");            if (i == 0) i = 3;              }  }    else if (Data[0-7] == 0x43) {     // motor 3 forward: ascii C        // select motor with multiplexer    PTBD = 0x02;                           // Send 0,1,0 to mux        for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i++];                       loopCount++;      asm ("nop");            if (i == 3) i = 0;           }     }    else if (Data[0-7] == 0x63) {     // motor 3 backward: ascii c      // select motor with multiplexer    PTBD = 0x02;                     // Send 0,1,0 to mux          for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i--];      loopCount++;      asm ("nop");            if (i == 0) i = 3;              }  }    else if (Data[0-7] == 0x44) {     // motor 4 forward: ascii D        // select motor with multiplexer    PTBD = 0x03;                           // Send 0,1,1 to mux        for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i++];                       loopCount++;      asm ("nop");            if (i == 3) i = 0;           }     }    else if (Data[0-7] == 0x64) {     // motor 4 backward: ascii d      // select motor with multiplexer    PTBD = 0x03;                     // Send 0,1,1 to mux          for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i--];      loopCount++;      asm ("nop");            if (i == 0) i = 3;              }  }    else if (Data[0-7] == 0x45) {     // motor 5 forward: ascii E           // select motor with multiplexer    PTBD = 0x04;                           // Send 1,0,0 to mux        for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i++];                       loopCount++;      asm ("nop");            if (i == 3) i = 0;           }     }    else if (Data[0-7] == 0x65) {     // motor 5 backward: ascii e      // select motor with multiplexer    PTBD = 0x04;                     // Send 1,0,0 to mux          for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i--];      loopCount++;      asm ("nop");            if (i == 0) i = 3;              }  }    else if (Data[0-7] == 0x46) {     // motor 6 forward: ascii F           // select motor with multiplexer    PTBD = 0x05;                           // Send 1,0,1 to mux        for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i++];                       loopCount++;      asm ("nop");            if (i == 3) i = 0;           }     }    else if (Data[0-7] == 0x66) {     // motor 6 backward: ascii f      // select motor with multiplexer    PTBD = 0x05;                     // Send 1,0,1 to mux          for (loopCount = 0; loopCount <= Data[8-15]; loopCount++) {            PTDD = motorData[i--];      loopCount++;      asm ("nop");            if (i == 0) i = 3;              }  }  }

 Thanks

标签 (1)
0 项奖励
回复
8 回复数

1,563 次查看
bigmac
Specialist III

Hello,

 

The following example code shows how the SPI module might be used, as previously proposed.  There are two functions, one for frequent execution within the main loop, for processing all six steppers, and another for setting a new destination position for a specific stepper.

 

/****************************************************************************** File: Unipolar.h UNIPOLAR STEPPER MOTOR POSITION CONTROL ******************************************************************************/#ifndef _UNIPOLAR_H#define _UNIPOLAR_H#include "derivative.h"// Adjust to suit GPIO pin used.#define STROBE_HI  PTDDD_PTDDD0 = 1; PTDD_PTDD0 = 1#define STROBE_LO  PTDDD_PTDDD0 = 1; PTDD_PTDD0 = 0#define STEP_DELAY 5   // 5ms delay between steps (200 steps/s)// Stepper motor control structure:struct stepctrl {   byte phase;   int  stepcount;   byte timeout;};union SPI_buf {   byte B[3];   // Individual bytes for SPI send   dword D;};/******************************************************************************/// Function prototypes:// Stepper motor control update - to be frequently called within main loopvoid control_update( void);// Setup motor destinationvoid set_destination(     byte id,    // 'A' to 'F' -> motor number 0 - 5, forward direction                 // 'a' to 'f' -> motor number 0 - 5, reverse direction     int steps); // number of steps#endif  /* _UNIPOLAR_H */

 

/****************************************************************************** File: Unipolar.c UNIPOLAR STEPPER MOTOR POSITION CONTROL ******************************************************************************/#include "Unipolar.h"#include "SPI.h"                  // SPI_send() macro definition#include "TPM.h"                  // Declaration for 'tflag'// Static variables:static struct stepctrl motor[6];  // Array of structuresstatic union SPI_buf mcontrol;// Stepper motor control sequence table:const byte motor_phase[] = { 0x09, 0x0A, 0x06, 0x05 };/******************************************************************************/// Stepper motor control update// To be frequently called within the main loop.void control_update( void){  byte i, c, s;  if (tflag) {                              // Ready for further processing    tflag = 0;                              // Clear timing flag    for (i = 0; i < 6; i++) {               // Process each stepper motor      if (motor[i].timeout == 0) {          // Ready for next step        if (motor[i].stepcount) {           // Motor needs to step          motor[i].timeout = STEP_DELAY;    // Delay to next step after this one          if (motor[i].stepcount > 0) {     // Forward step            motor[i].stepcount--;            c = (++motor[i].phase) & 0x03;  // Next phase          }          if (motor[i].stepcount < 0) {     // Reverse step            motor[i].stepcount++;            c = (--motor[i].phase) & 0x03;  // Next phase          }          s = (byte)(4 * i);                    // Shift value          mcontrol.D &= ~(0x0F << s);           // Clear motor control value          mcontrol.D |= (motor_phase[c] << s);  // Place new control value        }      }      else  motor[i].timeout--;    }    // Update control for all motors    SPI_send( mcontrol.B[0]);    SPI_send( mcontrol.B[1]);    SPI_send( mcontrol.B[2]);    STROBE_HI;    STROBE_LO;  }}/******************************************************************************/// Setup motor destination// id    - 'A' to 'F' represents motor number 0 - 5, forward direction//       - 'a' to 'f' represents motor number 0 - 5, reverse direction// steps - number of stepsvoid set_destination( byte id, int steps){  if (id >= 'A' && id <= 'F') {    id -= 'A';                      // Value 0 - 5    motor[id].stepcount += steps;  }  if (id >= 'a' && id <= 'f') {    id -= 'a';                      // Value 0 - 5    motor[id].stepcount -= steps;  }}

 

The code assumes a 1 millisecond tick rate from the TPM1 module (software output compare mode), and provides 5 millisecond spacing between successive steps.

 

I do not understand the format of your data packets, particularly how the number of steps is represented.  If by a sequence of ASCII digits, these would need to be separately converted to a binary value, for use by the set_destination() function.

 

I could not work out your intent with the handler for the SCI2 received data.  I would tend to place the incoming data into a FIFO buffer, and then parse the packet data from within the buffer, from outside the ISR handler.

 

Regards,
Mac

 

0 项奖励
回复

1,563 次查看
JimDon
Senior Contributor III

BTW - I think you  meant to use a a 74LS138 1 of 8 decoder, not 74LS151 8 input multiplexer.

 

 

0 项奖励
回复

1,563 次查看
woohoo
Contributor I

Thanks for the help so far

 

I now realise that the motors are going to need a time delay between each step ans that the 74LS151 would not have been able to handle the current and also the 74LS138 is the correct IC to use, however I can't use SPI as bigmac suggested as I am using it to communicate with a CAu10 piezo amplifier, so instead I used another ULN2003A transistor array to suply the current as shown in the attached diagram.

 

 

0 项奖励
回复

1,563 次查看
bigmac
Specialist III

Hello,

 

Some further observations on your proposed method -

 

  1. The use of LS logic devices is incompatible with the 9s08GT16 MCU.  The LS devices require a supply voltage within the limits 4.5 to 5.5 volts.  You will need to substitute equivalent HC devices, that are capable of operation at 3 volts.
  2. The 74HC237 device is an alternative 3-to-8 line decoder that has active high outputs.
  3. Since the individual sections of the ULN2003 are Darlington transistors, their saturation voltage will be quite high, say more than 1 volt.  Since two transistors are effectively series connected, this will reduce the voltage applied to the motor by more than 2 volts.  A further sideeffect is that the "upper" transistor that connects to the motor winding may not fully turn on.  The switching voltage level would be equivalent to the normal switching voltage plus the saturation voltage of the lower transistor.  This may possibly exceed the 3 volt level available at the MCU output.
  4. It seems that your intent is that the motor windings should be de-energised when not stepping (relying on the low indent torque to maintain the last position).  If so, I would think that the previous winding currents would need to be re-established, before then commencing to step.
  5. Your method seems implicit that only one motor may move at any time, and the stepping needs to complete before another motor may commence movement.  This restriction does not apply to the SPI approach.

 

Regards,

Mac

 

0 项奖励
回复

1,563 次查看
bigmac
Specialist III

Hello,


woohoo wrote:

however I can't use SPI as bigmac suggested as I am using it to communicate with a CAu10 piezo amplifier, 

 


Provided the SPI communications with the piezo amplifier is not time critical, the same SPI module and its associated bus, may be easily shared between the two slave devices.  If the SPI communications for the amplifier is done within the main loop, this is very likely to be so.

 

The amplifier has a /SYNC input  line, and will be insensitive to bus activity unless this is set to active low.  Similarly, the shift register outputs would not be updated until a strobe pulse occurs.

 

Regards,

Mac

 

0 项奖励
回复

1,563 次查看
JimDon
Senior Contributor III

 

Well, you will need inverters on the outputs of the 74LS138, as they are low true and the ULN2003 is inverting.

 

"The decoder accepts three binary weighted inputs (A0, A1, A2)and when enabled provides eight mutually exclusive active LOW Outputs (O0 – O7)."

 

0 项奖励
回复

1,563 次查看
bigmac
Specialist III

Hello,

 

You would definitely need to latch the current state of each motor.  Additionally, it is inappropriate to switch the ground pin of each ULN2003 driver, using the 74LS151.  The full motor current will flow through the ground pin, and this will overload the multiplexer device.

 

To accomplish your aims, I might consider using the SPI module in conjunction with a "daisy-chain" of three 74HC595 shift register devices.  These will provide for the latching of 24 output lines.  You will still need to follow with the ULN2003's to provide a buffer for each motor winding.  The output latch strobe (RCK) input to each '595 would be paralleled, and directly controlled with a further output line from the MCU.

 

From a firmware perspective, the current state of the six motors would be represented by a sequence of three bytes, with each byte controlling two motors.  Whenever one or more motors is incremented or decremented to the next state, the three byte sequence would be updated, and then sent via the SPI.  After the three bytes have been sent, the output latches would then be strobed.

 

To determine the next state in the stepping sequence, you might consider using a lookup table to represent the stepping mode you are using.

 

Yes, you would also need to control the minimum period between each update, to be consistent with the maximum stepping rate for the motors.  After each update you would set a timout period, and prior to each update you would test whether the previous timeout period had already expired.  Here I assume that the primary requirement is for position control, rather than motor speed control.

  

Regards,

Mac

 

0 项奖励
回复

1,563 次查看
JimDon
Senior Contributor III

Well, there seems to be many potential problems, one big one being that it seems you are try to step the motors way too fast.

You need to use a timer and step the motors at an adjustable rate. I say adjustable, because in my experience different steppers respond differently to step rates.

Also, you should probably save the last step state for each motor and resume from that state, or you will get odd stepping behavior.

 

 

0 项奖励
回复