acquire stepper motor steps and display as distance

Thread Starter

graham2550

Joined Jun 24, 2014
14
Hi everyone.
I'm building a 3 axis router/mill using nema23 steppers.
The parallel breakout board has outputs of step and direction for all axis.
I want to utilize this to build a display unit (7 segs) showing where the axis is in respect to home or zero.
I coded a PIC18F2550 to do this and utilized the MAX7219 display driver to output to the 7 x 7 segment displays using hardware SPI.
I use INT2 interrupt to capture the step pulse and convert to millimeters at 1/1000mm resolution. Each step travels the axis 0.025mm.
THE PROBLEM I HAVE: on the breadboard setup I can use my signal generator to input the pulses (5v square wave 20% duty).
unfortunately anything over ~600Hz the display does not increment any faster, it still displays but not going any faster.
I need to be able to get up to 4kHz and display in real time as the axis can move at 100mm/sec which is 4000 steps/sec
I'm running the uP at 48MHz which at Fosc/4 is 12MHz = 83nS per instruction. 4kHz is 250uS and I feel there is plenty of time to do the calcs and display between interrupts.
Any help and suggestions greatly appreciated.
circuit and code below. I'm using MikroC pro for PIC.



Code:
//**************** // CODE FOR X & Y AXIS ******************************
//****************************************************************************
// Stepper motor step counter, converted to millimeter and display
// For PIC 18F2550
// EXT osc @ 20MHz PLL to 48MHz
// Graham Payne commenced 17/7/2015
//****************************************************************************

//Port Declarations
//****************************************************************************
sbit X_Step at RB2_bit;                //step input external INT2
sbit X_Dir at RA0_bit;                 //direction input
sbit X_Step_Direction at TRISB2_bit;
sbit X_Dir_Direction at TRISA0_bit;

// SPI module connections for MAX7912
sbit CS at RC0_bit;
sbit Spi_SDO at RC7_bit;
sbit Spi_CLK at RB1_bit;
sbit Spi_SDI at RB0_bit;
sbit CS_direction at TRISC0_bit;
sbit Spi_SDO_Direction at TRISC7_bit;
sbit Spi_CLK_Direction at TRISB1_bit;
sbit Spi_SDI_Direction at TRISB0_bit;

//****************************************************************************
//Variables
//****************************************************************************
const unsigned long Step_MM = 25;   //step amount in millimeters x 1000
unsigned long MM;
unsigned char init_MAX_Flag;

//****************************************************************************
//Send character to the MAX7219
void send_to_max7219(char address, char dat)
{
  CS = 0;                             // LOAD(CS) LINE LOW
  SPI1_Write(address);
  SPI1_Write(dat);
  CS = 1;                             // LOAD(CS) LINE HIGH
}

//****************************************************************************
//initiallise the MAX7219
void init_MAX7219() {
  send_to_max7219(0x09,0xFF);         // BCD Mode Code B
  send_to_max7219(0x0A,0x09);         // INTENSITY 19/32 th's
  send_to_max7219(0x0B,0x06);         // SCAN 7 DIGITS
  send_to_max7219(0x0C,0x01);         // TURN ON
  send_to_max7219(0x0F,0x00);         // TEST OFF
}

//****************************************************************************
void Step()
{
    if(init_MAX_Flag == 0){           //have to init MAX7219 here as cannot have 2 threads.
     init_MAX7219();
     init_MAX_Flag = 1;
     delay_ms(1);
     }

    //check direction, add/subtract a Step amount (*1000, no problems with decimal places & rounding errors)
    if(X_Dir == 1) {MM += Step_MM;} else {MM -= Step_MM;}

    //if(MM < 0) MM = fabs(MM);                   //if goes negative

    send_to_max7219(0x01, MM/1000000);          //extract 1000s
    send_to_max7219(0x02, (MM/100000)%10);      //extract 100s
    send_to_max7219(0x03, (MM/10000)%10);       //extract 10s
    send_to_max7219(0x04, ((MM/1000)%10)+128);  //extract 1s, add decimal point
    send_to_max7219(0x05, (MM/100)%10);         //extract .1s
    send_to_max7219(0x06, (MM/10)%10);          //extract .01s
    send_to_max7219(0x07, MM%10);               //extract .001s

}

//****************************************************************************
void interrupt()
{
  if(INT2IF_bit == 1)
  {
    INT2IF_bit = 0;                       // clear the flag
    Step();                               // do a step and calculate output
  }
}

//****************************************************************************
void main() {

  CMCON = 7;                              //turn off comparators
  ADCON0 = 0b00000000;                    //turn off A/D converters
  ADCON1 = 0b00001111;                    //all digital

  PORTA = 0;
  PORTB = 0;
  PORTC = 0;
  TRISA = 0b00000001;                     //RA0 dir input
  TRISB = 0b00000100;                     //RB2 int2 for step input
  TRISC = 0b00000000;

  SPI1_Init();                            // Initialize SPI for MAX7912
  delay_ms(100);

  MM = 0;
  init_MAX_Flag = 0;

  GIE_bit = 1;                            // global interrupt enabled
  INTEDG2_bit = 1;                        // rising edge detection on INT2
  INT2IF_bit = 0;                         // clear INT2 flag
  INT2IE_bit = 1;                         // INT2 enabled

  do {

  } while(1);

}
 

Attachments

Last edited:

tshuck

Joined Oct 18, 2012
3,534
Your interrupt routine is huge!

I wouldn't be surprised if the length of your ISR was the limiting factor here.

You aren't doing anything in the main loop once you've initialized the device, use that to calculate the data and send it outout to the display. Use the interrupt to increment the counter (MM) and do all of the heavy lifting in main().

Always keep your ISRs to a minimum.
 

JWHassler

Joined Sep 25, 2013
308
The display unit doesn't need to update the display at 4KHz, it just has to not miss any steps... the ISR catches the steps, and the display-routine can run as leisurely as it needs to. It's just for you to see, and you can't follow it if it's moving
 

tshuck

Joined Oct 18, 2012
3,534
The display unit doesn't need to update the display at 4KHz, it just has to not miss any steps... the ISR catches the steps, and the display-routine can run as leisurely as it needs to. It's just for you to see, and you can't follow it if it's moving
...and the count and the display is done within the ISR...
 

MaxHeadRoom

Joined Jul 18, 2013
30,654
Why reinvent the wheel, Mach3 Mach4 is a cheap way to do it?
Otherwise you need a trajectory planner for axis interpolation etc?
Max.
 

Thread Starter

graham2550

Joined Jun 24, 2014
14
The display unit doesn't need to update the display at 4KHz, it just has to not miss any steps... the ISR catches the steps, and the display-routine can run as leisurely as it needs to. It's just for you to see, and you can't follow it if it's moving
Thanks JWHasler, I think That is the solution.
I will just catch the steps and increment the MM variable in the ISR and put the all the calcs and display routine in the while loop.

Just to be clear, the incoming frequency varies from 0 (stopped) to 4kHz (max feedrate).
I am using Mach3 of course, what else is there.
But I wanted a heads-up display on the machine, I am not always sitting at the monitor.
I will get back to you and let you all know the results.
 

Thread Starter

graham2550

Joined Jun 24, 2014
14
The display unit doesn't need to update the display at 4KHz, it just has to not miss any steps... the ISR catches the steps, and the display-routine can run as leisurely as it needs to. It's just for you to see, and you can't follow it if it's moving
I tried your suggestion and it works great. Thanks.
I had to flag the display routine on the interrupt as there are timing constraints on the MAX7219.
Sometimes you cant see the wood for the trees!
New code:
Code:
//**************** // CODE FOR X or Y AXIS ******************************
//****************************************************************************
// Aquire Stepper motor steps, convert to millimeter and Display.
// Suitable 1 axis only.
// For PIC 18F2550
// EXT osc @ 20MHz PLL to 48MHz
// Graham Payne commenced 17/7/2015
//****************************************************************************

//Port Declarations
//****************************************************************************
sbit X_Step at RB2_bit;                //step input external INT2
sbit X_Dir at RA0_bit;                 //direction input
sbit X_Step_Direction at TRISB2_bit;
sbit X_Dir_Direction at TRISA0_bit;

// SPI module connections for MAX7912
sbit CS at RC0_bit;
sbit Spi_SDO at RC7_bit;
sbit Spi_CLK at RB1_bit;
sbit Spi_SDI at RB0_bit;
sbit CS_direction at TRISC0_bit;
sbit Spi_SDO_Direction at TRISC7_bit;
sbit Spi_CLK_Direction at TRISB1_bit;
sbit Spi_SDI_Direction at TRISB0_bit;

//****************************************************************************
//Variables
//****************************************************************************
const unsigned long Step_MM = 25;   //step amount in millimeters x 1000
unsigned long MM;
unsigned char Flag;


//****************************************************************************
//Send character to the MAX7219
void send_to_max7219(char address, char dat)
{
  CS = 0;                             // LOAD(CS) LINE LOW
  SPI1_Write(address);
  SPI1_Write(dat);
  CS = 1;                             // LOAD(CS) LINE HIGH
}

//****************************************************************************
//initiallise the MAX7219
void init_MAX7219() {
  send_to_max7219(0x09,0xFF);         // BCD Mode Code B
  send_to_max7219(0x0A,0x09);         // INTENSITY 19/32 th's
  send_to_max7219(0x0B,0x06);         // SCAN 7 DIGITS
  send_to_max7219(0x0C,0x01);         // TURN ON
  send_to_max7219(0x0F,0x00);         // TEST OFF
}

//****************************************************************************
void interrupt()
{
  if(INT2IF_bit == 1)
  {

    INT2IF_bit = 0;                       // clear the flag
    //check direction, add/subtract a Step amount (*1000, no problems with decimal places & rounding errors)
    if(X_Dir == 1) {MM += Step_MM;} else {MM -= Step_MM;}

    //if(MM < 0) MM = fabs(MM);                   //if goes negative
    Flag = 1;
  }
}

//****************************************************************************
void Display()
{
    send_to_max7219(0x01, MM/1000000);          //extract 1000s
    send_to_max7219(0x02, (MM/100000)%10);      //extract 100s
    send_to_max7219(0x03, (MM/10000)%10);       //extract 10s
    send_to_max7219(0x04, ((MM/1000)%10)+128);  //extract 1s, add decimal point
    send_to_max7219(0x05, (MM/100)%10);         //extract .1s
    send_to_max7219(0x06, (MM/10)%10);          //extract .01s
    send_to_max7219(0x07, MM%10);               //extract .001s
}

//****************************************************************************
void main() {

  CMCON = 7;                              //turn off comparators
  ADCON0 = 0b00000000;                    //turn off A/D converters
  ADCON1 = 0b00001111;                    //all digital

  PORTA = 0;
  PORTB = 0;
  PORTC = 0;
  TRISA = 0b00000001;                     //RA0 dir input
  TRISB = 0b00000100;                     //RB2 int2 for step input
  TRISC = 0b00000000;

  SPI1_Init();                            // Initialize SPI for MAX7912
  delay_ms(100);
  init_MAX7219();
  delay_ms(100);
 
  MM = 0;

  GIE_bit = 1;                            // global interrupt enabled
  INTEDG2_bit = 1;                        // rising edge detection on INT2
  INT2IF_bit = 0;                         // clear INT2 flag
  INT2IE_bit = 1;                         // INT2 enabled
 
  do {

    if (Flag)
    {
      Flag = 0;
      Display();
    }
   
  } while(1);
 
}
 
Top