Saturday, 10 September 2011

Bipolar Stepper Motors - making them work.

An opportunity came to hande recently - an old PC was scrapped and it had a DVD drive in ti. I took the chance to totally disassemble it and discovered a lovely stepper motor with gearing and so forth to make it into a linear actuator.



The problem was, how to drive the beast. Firstly, it was obviously a stepper motor as it had > 2 leads coming from it. How was it wired up, though. Well, firstly I got a voltmeter and worked out that it was simply to 20 ohm coils. This means it was a dreaded bipolar stepper - harder to drive than unipolar.

I decided to have a go. I soldered on some power leads and hooked it up to my DFRobot L298 Motor shield. Then, I had to write the code.

It turns out that there is no ready source code for this handy. Basically, the motor has to be driven by a dual h-bridge arrangement working the coils using digital square waves that go between +5 and -5V through 0V. This can be seen roughly as sine waves. They waves need to be about 90 degress out of phase.


Basically, the DFRobot board allows you to control the speed and direction of each motor only. I built a function to set speeed and direction of each motor based on the steps 0->7 in the diagrams. Each step waas a function. I put these functions into an array of function pointers and simply called them in order to get movement. Stepping back throught the table gave reverse motion.

Here's the simple code:

//Arduino PWM Speed Control:
int E1 = 6;  
int M1 = 7;
int E2 = 5;                        
int M2 = 4;  

#define  DELAY  2
#define  PWR    200

void
MotorsOff()
{
    analogWrite(E1, 0);   //PWM Speed Control
    analogWrite(E2, 0);   //PWM Speed Control
 
}

void
Step0()
{
    // 0
    digitalWrite(M1,HIGH);  
    analogWrite(E1, PWR);   //PWM Speed Control

    digitalWrite(M2, HIGH);      
    analogWrite(E2, 0);   //PWM Speed Control
    delay(DELAY);
}
void
Step1()
{
    // 1
    digitalWrite(M1,HIGH);  
    analogWrite(E1, PWR);   //PWM Speed Control

    digitalWrite(M2, HIGH);      
    analogWrite(E2, PWR);   //PWM Speed Control
    delay(DELAY);
}
void
Step2()
{
    // 2
    digitalWrite(M1,HIGH);  
    analogWrite(E1, 0);   //PWM Speed Control

    digitalWrite(M2, HIGH);      
    analogWrite(E2, PWR);   //PWM Speed Control
    delay(DELAY);
}
void
Step3()
{   
    // 3
    digitalWrite(M1,LOW);  
    analogWrite(E1, PWR);   //PWM Speed Control

    digitalWrite(M2, HIGH);      
    analogWrite(E2, PWR);   //PWM Speed Control
    delay(DELAY);
}
void
Step4()
{   
    // 4
    digitalWrite(M1,LOW);  
    analogWrite(E1, PWR);   //PWM Speed Control

    digitalWrite(M2, HIGH);      
    analogWrite(E2, 0);   //PWM Speed Control
    delay(DELAY);
}
void
Step5()
{   
     // 5
    digitalWrite(M1,LOW);  
    analogWrite(E1, PWR);   //PWM Speed Control

    digitalWrite(M2, LOW);      
    analogWrite(E2, PWR);   //PWM Speed Control
    delay(DELAY);
}
void
Step6()
{   
     // 6
    digitalWrite(M1,LOW);  
    analogWrite(E1, 0);   //PWM Speed Control

    digitalWrite(M2, LOW);      
    analogWrite(E2, PWR);   //PWM Speed Control
    delay(DELAY);
}
void
Step7()
{   
      // 7
    digitalWrite(M1,HIGH);  
    analogWrite(E1, PWR);   //PWM Speed Control

    digitalWrite(M2, LOW);      
    analogWrite(E2, PWR);   //PWM Speed Control
    delay(DELAY);
}

#define  NUM_STEPS  8

int *steps[NUM_STEPS] = {(int *)Step0,(int *)Step1,(int *)Step2,(int *)Step3,(int *)Step4,(int *)Step5,(int *)Step6,(int *)Step7};


void setup()
{
    pinMode(M1, OUTPUT);  
    pinMode(M2, OUTPUT);
}

void loop()
{
  int  i;
  void  (*j)();
 
  for (i = 0; i < 450; i++)
  {
   
    j = (void (*)())steps[i % NUM_STEPS];
   
    (*j)();
  }
 
  MotorsOff();
  delay(1000);
 
  for (i = 450; i > 0; i--)
  {
   
    j = (void (*)())steps[i % NUM_STEPS];
   
    (*j)();
  }

 
}

Wednesday, 7 September 2011

48 bit Shift Register

One of the problems with running a model railroad from a microcontroller is the need for lots of digital output pins. There are a variety of things that need turning on and off - signals, track sections/blocks, house lights, even driving stepper motors!

This simple circuit uses 6 x 74LS595 chips to make a total of 48 bits of digital output using only 3 digital outs on my Arduino.

Here's video of it in action. I've hooked up my el-cheapo opto-isolated relay card to the lower 4 bits just to demonstrate it working.


You can see the entire setup above - the Arduino, the breadboard with the 6 chips and my relay board.
 Below you can see a little detail of the first few chips.

There is a really good Arduino tutorial here: http://www.arduino.cc/en/Tutorial/ShiftOut


The real pain in the a*#e is the amount of fiddly little wires I have had to cut, strip and place to make this work. It has to be made on a PCB before I do anything else!
And here is the source for my test program. I can feel an Arduino library coming one...


//Pin connected to ST_CP of 74HC595
int latchPin = 10;
//Pin connected to SH_CP of 74HC595
int clockPin = 11;
////Pin connected to DS of 74HC595
int dataPin = 9;

#define  BITS_PER_BYTE  8

#define  NUM_BYTES  4
byte  bitset[NUM_BYTES];

void
BitsetClear()
{
    int  i; 
   
    for (i = 0; i< NUM_BYTES; i++)
       bitset[i] = 0;
}
void
BitsetSet()
{
    int  i; 
   
    for (i = 0; i< NUM_BYTES; i++)
       bitset[i] = 0xFF;
}

void
BitsetSetBit(int  n)
{
    int  index;
    int  bit_num;
   
    index = n/BITS_PER_BYTE;
   
    bit_num = n % BITS_PER_BYTE;
   
    Serial.print("S: ");
    Serial.print(n);
    Serial.print(" : ");
    Serial.print(index);
    Serial.print(", ");
    Serial.println(bit_num);
   
    bitset[index] = bitset[index] | (0x01 << bit_num);
}

void
BitsetClearBit(int  n)
{
    int  index;
    int  bit_num;
   
    index = n/BITS_PER_BYTE;
   
    bit_num = n % BITS_PER_BYTE;
   
    Serial.print("C: ");
    Serial.print(n);
    Serial.print(" : ");
    Serial.print(index);
    Serial.print(", ");
    Serial.println(bit_num);
   
    bitset[index] = bitset[index] & ~(0x01 << bit_num);
}

void
BitsetDisplay()
{
    int  i; 
    for (i = NUM_BYTES; i > 0; i--)
    {
      digitalWrite(latchPin, LOW);
      // shift out the bits:
      shiftOut(dataPin, clockPin, MSBFIRST, bitset[i-1]); 
      //take the latch pin high so the LEDs will light up:
      digitalWrite(latchPin, HIGH);
     
      Serial.print(" ");
      Serial.print(bitset[i - 1], BIN );
     }
     Serial.println(".");
}



void setup() {
   
  Serial.begin(9600);
 
  //set pins to output so you can control the shift register
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
 
  BitsetSet(); 
  BitsetDisplay();
}

void loop() {

  int  i;
 
  int  num_bits = 4;// NUM_BYTES * 8;

  for( i = 0; i < num_bits ; i++)
  {
    if (i==0)
    {
      BitsetSetBit(num_bits - 1);
      BitsetClearBit(i);
      BitsetDisplay();
    }
    else
    {
      BitsetSetBit(i - 1);
      BitsetClearBit(i);
      BitsetDisplay();
    }
    delay(200);
  }
}

Sunday, 4 September 2011

Track Block Section Design

I'm now starting to think about how I can actually build my layout and, given no DCC, I need to use track block sections to control the trains. I've come up with a simple design and I've documented it below.
I'll need about 6 of these to control my planned layout. Darn - needs a bigger arduino with more sensors.


Arduino and PWM for model trains...

Noisy!
You may have noticed from my videos that the trains on my test layout make a hideous high pitched whine. This is due to the PWM (pulse width modulation) frequency used by the Arduino analogWrite function.


Solved
If you call this function that I found in the Arduino Playground, all is fixed: http://www.arduino.cc/playground/Code/PwmFrequency

Note that the default divisor for pins 5 & 6 is 64 so the timers need to be adjusted to cope with the change. 

Choosing 1024 gave the best result so I had to divide all timer functions by 1024/64 = 16.