5 Phase Stepper Driver v.2

I’ve got some unfinished business with my 5 phase stepper motors.  Thought of giving it another try, this time with an Arduino which I had lying around.

With a new Design of L298s driving the phases instead of IRF540s, the results are much better than before.

5-phase-stepper-v2-schematic
Schematic – 5 phase stepper driver v2

The arduino drives the 5 phases according to the same sequence as in my previous atempt (ref. http://www.cs.uiowa.edu/~jones/step/types.html#multiphase). A PWM of 30kHz at 55% duty Cycle keeps the current through the phases at a manageable level of 2A. Digital pins 2-6 of the Arduino drive the phases while Digital pin 10 drives the enable pins of the L298 with the PWM signal. Digital Pins 12, and 13 are the step and direction inputs. The code can currently only do full step.

20160612_223430

Code.


const int phaseA = 2;
const int phaseB = 3;
const int phaseC = 4;
const int phaseD = 5;
const int phaseE = 6;

const int stepInput = 12;
const int dirInput = 13;
const int fullHalfInput = 8;

int eSeq[] = {
  B01011000, // 88
  B01001000, // 72
  B01101000, // 104
  B00101000, // 40
  B00101100, // 44
  B00100100, // 36
  B00110100, // 52
  B00010100, // 20
  B01010100, // 84
  B01010000, // 80
};

int currentPos = 0;
int delayTime = 975; // by trial and error

int lastStepState = 0;

void setup() {
  // initialize the serial port:
  Serial.begin(9600);
  
  DDRD = B01111100;
  DDRB = B00000110; 

  TCCR1A = _BV(COM1A1) | _BV(COM1B1) ; // phase and frequency correct mode. NON-inverted mode
  //phase/frequency correct mode. SELECT THIS FOR INVERTED OUTPUTS.
  TCCR1B = _BV(WGM13) | _BV(CS10);
  
  ICR1=265; // 400 for 20kHz, 265 for 30.1kHz
  int dc = 55.0;
  
  OCR1B = int((dc/100.0)*ICR1);
  OCR1A = int((dc/100.0)*ICR1);
}

void loop() {
/* 
  // Uncomment for continuous operation.
  while(1){
    int pos = getNextPos();
   
    PORTD = B11111111 & eSeq[pos];
    //Serial.println(PORTD);
    delayMicroseconds(delayTime);
  }
*/

  // Full step sequence drive
  int currentStepState = digitalRead(stepInput);
  if(currentStepState != lastStepState){
    if( currentStepState == HIGH ){
      //Serial.println("step on");
      int pos = getNextPos();
      PORTD = B11111111 & eSeq[pos];
      delayMicroseconds(delayTime);
      //Serial.println(eSeq[pos]);
    }
  }
  
  lastStepState = currentStepState;
  
}

int getNextPos(){
  if(digitalRead(dirInput) == HIGH){
    currentPos = currentPos+1;
    if(currentPos > 9){
      currentPos = 0; 
    }
  }else{
    currentPos = currentPos-1;
    if(currentPos<0){
      currentPos=9;
    }
  }
  
  return currentPos;
}
Next steps : 
Use the ADC to sense the voltage across the 0.5OHM resistor, and automatically control the current by adjusting the pwm.
Use the enable pins to emulate a 0V position to do 4-phase and 4-5phase drives as in http://www.orientalmotor.com/technology/articles/2phase-v-5phase.html

References : 
http://www.oxgadgets.com/2011/04/creating-a-variable-frequency-pwm-ouhttps://easyeda.comtput-on-arduino-uno.html
https://sites.google.com/site/qeewiki/books/avr-guide/pwm-on-the-atmega328

Schematics drawn using : https://easyeda.com online schematic tool

Portable Configurator for 2.4GHz Computerized CT6A / B Radios – Part 1

It’s been a while since my last post, and I’ve been busy like a bee the past few years, with my job, masters degree and a new startup business.

Last few days I’ve got some time to put in to work on an idea which I had for a long time. Its a portable handheld configurator for the CT6A radio.

http://www.hobbyking.com/hobbyking/store/__9042__Hobby_King_2_4Ghz_6Ch_Tx_Rx_V2_Mode_2_.html
http://www.leaderhobby.com/product.asp?ID=9394001220327

These radios are computer programmable and to even reverse a servo signal you need to plug it to a computer and make changes through the t6config software.

My idea was to create an arduino based hand held configurator with an LCD display which I can then use to configure the radio in the flying field itself.

A few people have already developed similar handhelds, one for PalmV and an arduino based memory for the T6, but both didn’t seem to be what I need, Palm V is a good solution but I don’t have the device, secondly the arduino based solution wasn’t capable of changing settings on the fly. Hence I’m on my own…

The T6 Serial definitions became very useful (http://www.rcgroups.com/forums/showpost.php?p=20080351&postcount=4964) and saved a lot of reverse engineering time.

I have designed the circuit diagram and I’m now working on the software for the configurator.

The diagram is as follows.

T6 Configurator Schematic
T6 Configurator Schematic

The pin 10 and 11 connect to the T6 through a DIN4 Jack (S-Video), pin 11 being the TX which goes to the Rx pin of the T6 through the Din-4 Socket, and pin 10 being the RX should go to the TX Pin of the Din4 Socket. Need to connect the ground from the DIN Socket to the ground of the circuit. Refer http://aeroquad.com/showthread.php?2587-T6-TX-Cable-alternative&p=24855&viewfull=1#post24855 for the Din-4 Socket connections

I have written the following code so far, a little buggy and I’m stuck at reading the existing configuration, as the T6 doesn’t seem to be returning me exactly 69 bytes of data. Some times I read 85, some times 67 and some times 68 but never 69. If someone know why and can help me out on this, please comment. Your help is greatly appreciated.


#include
#include

SoftwareSerial mySerial(10, 11); // RX, TX
LiquidCrystal lcd(13, 12, 5, 4, 3, 2);

const int arrowChar = B01111110;

const int LINE_COUNT = 2;

const int NO_MENU = 0;
const int MAIN_MENU = 1;
const int SUB_MENU1 = 2;

int myInit[] = {
0x55,0x99};
int myDinit[] = {
0x55,0x88};
int configS[] = {
0x55,0xfd};
int readData[] = {
0x55,0xFA,0x0};
int currentSetting[] = {
0x55, 0xFF, 0x00, 0x15, 0x64, 0x32, 0x64, 0x32, 0x64, 0x32, 0x00, 0x00, 0x00, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x00, 0x43, 0x00, 0x34, 0x00, 0x32, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x43, 0x00, 0x2E, 0x00, 0x44, 0x00, 0x4C, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x64, 0x64, 0x02, 0x75, 0x64, 0x64, 0x02, 0x40, 0x5D, 0x32, 0x00, 0x04, 0x01, 0x9C, 0x01, 0x0C, 0xC6};
//byte invertD[] = {0x55,0xFF,0x00,0x2A,0x64,0x32,0x64,0x32,0x64,0x32,0x00,0x00,0x00,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x34,0x64,0x64,0x02,0x75,0x64,0x64,0x02,0x40,0x5D,0x32,0x00,0x00,0x00,0x00,0x00,0x09,0xA8};

char * mainMenu[] = {
"Download Data","Directions","End Points","Sub Trims","Dual Rates", "Stick Mode", "Type", "Mixing","Save","Upload Data"};
const int MAIN_MENU_SIZE = 10;

char ** currentMenu = mainMenu;
int currentMenuSize = MAIN_MENU_SIZE;
int currentMenuState=NO_MENU;

int menuIndex = 0; // menu base index (index of menu item that is displayed in the first line)
int cursorIndex = 0;

void setup()
{
lcd.begin(16,2);

// Open serial communications and wait for port to open:
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}

// set the data rate for the SoftwareSerial port
mySerial.begin(115200);
//mySerial.println("Hello, world?");

pinMode(6,INPUT);
pinMode(7,INPUT);
pinMode(8,INPUT);
pinMode(9,INPUT);

lcd.clear();

writeToT6(configS,2);

Serial.println("Ready to go");
}

void writeToT6(int data[],int dataSize){
Serial.print("Writing ");
for (int i=0; i<dataSize; i++)
{

Serial.print(data[i],HEX);
Serial.print(",");
mySerial.write(data[i]);
}
mySerial.flush();
}

void printMainMenu(){
lcd.clear();
currentMenuState=MAIN_MENU;
currentMenu = mainMenu;
currentMenuSize = MAIN_MENU_SIZE;
updateMenu();
}

void updateMenu(){
printMenu(currentMenu,currentMenuSize,currentMenuState);
}

void printMenu(char ** menu,int menuSize,int state){
lcd.clear();
currentMenuState=state;

for(int i=0;i");

if((menuIndex+i)>=menuSize){
break;
}
lcd.print(menu[menuIndex+i]);
}
}

void goDown(){
menuIndex++;
if(menuIndex >= MAIN_MENU_SIZE){
menuIndex=MAIN_MENU_SIZE-1;
}
updateMenu();
}

void goUp(){
menuIndex--;
if(menuIndex0){
// int dataItem = mySerial.read();
// }
//writeToT6(configS,2);
// mySerial.listen();
writeToT6(readData,3);
mySerial.listen();

// wait for data.
int timer=0;
while(mySerial.available()==0){
if(timer>200)
break;
delay(10);
timer++;

}

int offset = 0;
Serial.print("Reading : ");
while(mySerial.available()>0){
int dataItem = mySerial.read();
Serial.print(dataItem,HEX);
Serial.print(",");

currentSetting[offset] = dataItem;
offset++;
}
Serial.println("");
Serial.print("Read : ");
Serial.println(offset);

//writeToT6(myDinit,2);

lcd.clear();
if(offset==69){
lcd.print("Success !");
}
else{
lcd.print("Error Reading T6");
}
}

void loop() // run over and over
{

lcd.setCursor(0,0);

int up = digitalRead(6);
int back = digitalRead(7);
int s1 = digitalRead(8);
int down = digitalRead(9);

if(up==LOW){
lcd.clear();
goUp();
}
else if(down==LOW){
lcd.clear();
goDown();
}
else if(s1==LOW){
//Serial.write("Menu state:");
//Serial.write(currentMenuState);
if(currentMenuState==NO_MENU){
printMainMenu();
}
else if(currentMenuState==MAIN_MENU){
currentMenuState = SUB_MENU1;
switch(menuIndex){
case 0:
downloadData();
break;
}
}
}
else if(back==LOW){
switch(currentMenuState){
case MAIN_MENU:
lcd.clear();
currentMenuState=NO_MENU;
break;
case SUB_MENU1:
lcd.clear();
currentMenuState=MAIN_MENU;
menuIndex=0;
break;
default:
currentMenuState=NO_MENU;
lcd.setCursor(4,0);
lcd.print("T6 Field");
lcd.setCursor(2,1);
lcd.print("Configurator");
break;

}

}
else{
if(currentMenuState==NO_MENU){
lcd.clear();
lcd.setCursor(4,0);
lcd.print("T6 Field");
lcd.setCursor(2,1);
lcd.print("Configurator");
}
}

delay(100);

if(Serial.available()){
int resp = Serial.read();
if(resp==int('i')){
for (int i=0; i<2; i++)
{
Serial.write("Writing ");
Serial.write(myInit[i]);
mySerial.write(myInit[i]);
}
}
else if(resp==int('j')){
for (int i=0; i<2; i++)
{
Serial.write("Writing ");
Serial.write(myDinit[i]);
mySerial.write(myDinit[i]);
}

}
else if(resp==int('k')){
for (int i=0; i<sizeof(currentSetting); i++)
{
Serial.write("Writing ");
mySerial.write(currentSetting[i]);
}

}
}
}

For the above code to run properly I had to change the #define _SS_MAX_RX_BUFF 64 // RX buffer size in the SoftwareSerial.h file to #define _SS_MAX_RX_BUFF 128 // RX buffer size. Otherwise the arduino stops reading 63 bytes as the buffer overflows.

Further my findings are that you can use your arduino board as a computer cable for the arduino t6config software, but you need to remove the chip from the board or it will not work.

Will post more as I progress on this.

Cheers !

Getting the 5-phase stepper to run.

Recently I bought three stepper motors from a junk seller in ‘armour street’. It costed me Rs. 300 each. The labels on these motors were almost unreadable, but since I selected all three motors of the same type I was able to put the remaining contents of the labels to find out about the motors.

The labels stated the following.

5-PHASE STEPPING MOTOR
A5729-9115FV
DC 2.0A 0.347 OHMS / PHASE
0.72 * / STEP
FC6-0435 02
ORIENTAL MOTOR CO LTD

Each motor has 5 wires coming out, and by disassembling one motor I found it was wound in a penta wiring (All coils are connected end to end to form a pentagon).

The next job was to get the motor to spin. I searched the web for an appropriate controller but couldn’t find any that satisfied my need. So I decided to build my own.

I found the energizing sequence here.

I thought of using a PIC16F84 (since I had experimented with this chip and had one in my parts box) as the controller and five IRF540 s and five IRF9540 s as the driver MOSFETs.

I wrote the following code in C in order to spin the motor in a single direction. (My intension was to some how get this motor working)

#define __16f84

#include"pic/pic16f84.h"// Set the __CONFIG word:

typedef unsigned int word;

word at 0x2007  __CONFIG = 0x3ffa;

const unsigned int excitations[] = {22,18,26,10,11,9,13,5,21,20};

signed char excitation_index = 0;

 void forward(void){

 // increment the index;

 excitation_index++;

 PORTB = excitations[excitation_index%10];

}

void main(void) {

 TRISA = 0x00;

 TRISB = 0x00;

 PORTA = 0x00;

 PORTB = 0x00;

 T0CS = 0;

 T0IE = 0;

 GIE = 0;

 PSA = 0;

 PS0 = 1;

 PS1 = 1;

 PS2 = 1;

 T0IE = 1;

 GIE = 1;

 while(1){

 	// increment port a to indicate that the chip is doing it's job.

 	PORTA = PORTA + 1;

 }

}

void intr_handler(void) interrupt {

 T0IF = 0;

 GIE = 0;

 forward();

 GIE = 0;

}

I used the SDCC compiler for GNU/Linux to compile and used PIKDEV to burn the hex file to the chip. PIKDEV failed to verify the chip after burining the hex, so I switched to Windows and used ICProg to burn the chip which was successful.

The following is the basic driver schematic I used to run the motor.

Schematic

The Port B’s B0-B5 are connected to 1-5 in the diagram respectively. The motor connects to A,B,C,D,E points in the schematic.

I setup my proto board and powered it up with a 300mA power supply. It didnt work as expected. The motor kept vibrating without turning. After hours of checking what was wrong I figured out that the power was not enough to drive the motor.

Then I used a 1000mA 12V power supply to power the motor and the 300mA 12V to power the uC board.

The motor started to spin with very low torque and very low speed. Also about 2 of the IRF540N s were getting hotter than the others. The investigation is pending.

I think thats because there is no PWM used in this driver. Planing to add a PWM to this driver as the next modification.

By the way I destroyed 2 PIC’s in the process, one by applying 12Volts to PORTB and another by burning the hex file on to it too many times. ( I had used it for about more than a year and I suppose it went out of the maximum number of times it could be reprogrammed ). Any way I finally got the 5-phase to run. 🙂

See it working in the following video. Sorry about the very bad quality.