danjperron
Posts: 3411
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Micro-Stepping with the Raspberry PI using L298 driver

Tue Sep 09, 2014 2:20 am

Since I solve the problem of linearity , I decide to post the code on is own topic.

It is possible to do micro-stepping with PWM using a L298 driver.


This is an example on how to create 10 micro-step per step. This mean that your stepper has now 2000 step per revolution.

It is not perfect because the Raspberry Pi latency make it a little bit unstable but it does the job and the PWM signal is only 8 bits.

I connect the L298 like this

OUT1 Coil A+
OUT2 Coil A-
OUT3 Coil B+
OUT4 Coil B-

IN1 BCM GPIO 17 ( STEP_A in source code )
IN2 BCM GPIO 18 ( STEP_AN in source code)
IN3 BCM GPIO 22 ( STEP_B in source code)
IN4 BCM GPIO 25 ( STEP_BN in source code)

ENA BCM GPIO 23 (PWM_A in source code)
ENB BCM GPIO 24 (PWM_B in source code)

You could change the number of micro-step with the define MICROSTEP and the time delay with StepperDelay.

Micro-stepping will reduce vibration and create inner step. The L298 is not perfect and the PWM works because of stepper inertia. For better micro-stepping accuracy , it is preferable to use specialize driver like the SLA7026 with a D/A signal on the reference.

this is the code in C

Code: Select all

/************

Raspberry Pi Micro-stepping demo
using GPIO connected to a L298 I.C.

Author: Daniel Perron
Date:   7 Septembre 2014

External library  :
   PIGPIO    http://abyz.co.uk/rpi/pigpio/download.html

How to compile :

gcc -o stepper stepper.c  -lm -lpthread -lpigpio -lrt


GPL License

This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*******************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sched.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <pthread.h>
#include <pigpio.h>
#include <signal.h>


// =========   GPIO  IO DEFINITION

#define STEP_A  17
#define STEP_AN 18 
#define STEP_B  22
#define STEP_BN 25


#define PWM_A  23
#define PWM_B  24

#define MICROSTEP   10

long  StepperDelay = 50000/MICROSTEP;

#define STEP_PER_REVOLUTION 200



// BCM2708 DEFINITION  FOR MEMORY ACCESS



#define BCM2708_PERI_BASE        0x20000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

int  mem_fd;
void *gpio_map;

// I/O access
volatile unsigned long *gpio;

// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

// 32bit parallel mode  gpio handling

#define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
#define GPIO_READ  *(gpio + 13)



// ===========   STEPPER  DEFINITION



#define MICROSTEP_PER_REVOLUTION (STEP_PER_REVOLUTION * MICROSTEP)
#define CoilTableSize  (4 * MICROSTEP)



unsigned long StepperCoil[CoilTableSize];
unsigned char StepperAPWM[CoilTableSize];
unsigned char StepperBPWM[CoilTableSize];

unsigned long StepperMask = (1<<STEP_A) | (1<<STEP_AN) | (1<<STEP_B) | ( 1<<STEP_BN);

volatile long StepperPosition = 0;
volatile long TargetPosition = 0;
volatile int  StepperReadyFlag = 1;

pthread_mutex_t StepperThread_mutex;
pthread_mutex_t Stepper_mutex;
pthread_t StepperThreadId;

// ============   FUNCTION

void set_max_priority(void) {
  struct sched_param sched;
  memset(&sched, 0, sizeof(sched));
  // Use FIFO scheduler with highest priority for the lowest chance of the kernel context switching.
  sched.sched_priority = sched_get_priority_max(SCHED_FIFO);
  sched_setscheduler(0, SCHED_FIFO, &sched);
}

void set_default_priority(void) {
  struct sched_param sched;
  memset(&sched, 0, sizeof(sched));
  // Go back to default scheduler with default 0 priority.
  sched.sched_priority = 0;
  sched_setscheduler(0, SCHED_OTHER, &sched);
}



// ======= stepper thread



void SetStepper(unsigned long  StepperMicroStep)
{
  int  index = StepperMicroStep % CoilTableSize;

  GPIO_CLR= StepperMask & ~ StepperCoil[index];
  GPIO_SET= StepperCoil[index];
  gpioPWM(PWM_A, StepperAPWM[index]);
  gpioPWM(PWM_B, StepperBPWM[index]);
/*  printf("Position = %d Index = %d  PWA:%d PWB:%d",StepperMicroStep,index,StepperAPWM[index],StepperBPWM[index]);
  if(StepperCoil[index] & (1 << STEP_A)) printf(" A+ ");
  if(StepperCoil[index] & (1 << STEP_AN)) printf(" A- ");
  if(StepperCoil[index] & (1 << STEP_B)) printf(" B+ ");
  if(StepperCoil[index] & (1 << STEP_BN)) printf(" B- ");
  printf("\n");
*/
}


int exitFlag=0;
int ThreadReturn;
void*  StepperThreadFunction(void  * arg )
{
  long ltemp;
  
  set_max_priority();

  while(!exitFlag)
  {

   // Lock  position
   pthread_mutex_lock(&Stepper_mutex);

   if(TargetPosition == StepperPosition)
     {
        StepperReadyFlag=1;
        pthread_mutex_unlock(&Stepper_mutex);
        break;
      }
   StepperReadyFlag=0;
   if(TargetPosition < StepperPosition)
      StepperPosition--;
    else
      StepperPosition++;
    ltemp = StepperPosition;
    pthread_mutex_unlock(&Stepper_mutex);
    SetStepper(ltemp);
    // Set Current Position
    usleep(StepperDelay);
   }

//   printf("Thread close \n");
   set_default_priority();
   pthread_mutex_unlock(&StepperThread_mutex);
   pthread_exit(&ThreadReturn);
  return NULL;
}

//  FUNCTION


long GetStepperPosition(void)
{
  long ltemp;
  pthread_mutex_lock(&Stepper_mutex);
    ltemp = StepperPosition;
  pthread_mutex_unlock(&Stepper_mutex);
  return ltemp;
}


void MoveStepper(long value)
{
  // Lock mutex to  create Thread
  pthread_mutex_lock(&StepperThread_mutex);

  // Set  new Target position
  TargetPosition += value;

   // create the thread to move stepper
    int err = pthread_create(&StepperThreadId, NULL, &StepperThreadFunction, NULL);
        if (err != 0)
           {
             printf("\ncan't create thread :[%s]", strerror(err));
             pthread_mutex_unlock(&StepperThread_mutex);
           }
}



void BuildCoilTable(void)
{
  int loop;

  // Set first coil
  for(loop=0;loop<(CoilTableSize/2);loop++)
     StepperCoil[loop]= 1 << STEP_A;
  for(;loop<CoilTableSize;loop++)
     StepperCoil[loop]= 1 << STEP_AN;

  // Set second coil
  for(loop=0;loop<(CoilTableSize/2);loop++)
     StepperCoil[(loop + CoilTableSize/4) % CoilTableSize]|= 1 << STEP_B;
  for(;loop<CoilTableSize;loop++)
     StepperCoil[(loop + CoilTableSize/4) % CoilTableSize]|= 1 << STEP_BN;


   // Set Power to coil
   for(loop=0;loop<CoilTableSize;loop++)
    {
      unsigned char _temp;
      double sinV = fabs(sin(M_PI * loop / (CoilTableSize/2.0)));
      _temp= (unsigned char) floor( 255.0 * sqrt(sinV));
      StepperAPWM[loop]= _temp;
      StepperBPWM[(loop + MICROSTEP) % CoilTableSize]= _temp;
    }
}


void setup_io()
{
   /* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      exit(-1);
   }

   /* mmap GPIO */
   gpio_map = mmap(
      NULL,             //Any adddress in our space will do
      BLOCK_SIZE,       //Map length
      PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
      MAP_SHARED,       //Shared with other processes
      mem_fd,           //File to map
      GPIO_BASE         //Offset to GPIO peripheral
   );

   close(mem_fd); //No need to keep mem_fd open after mmap

   if (gpio_map == MAP_FAILED) {
      printf("mmap error %d\n", (int)gpio_map);//errno also set!
      exit(-1);
   }

   // Always use volatile pointer!
   gpio = (volatile unsigned long *)gpio_map;

} // setup_io

// exit handler.  Just kill the thread 
void   bye(void)
     {
       exitFlag=1;
       puts ("\nGoodbye, cruel world....");
       usleep(100000);
     }

// Control-C handler.  Somehow the pigpio unhandle it properly
void MyCtrlC(int sig)
{
  exitFlag=1;
}


int main(void)
{
   int loop;

   printf("Raspberry Pi micro-stepping demo with L298 driver\n");
   printf("(c) Daniel Perron Sept.2014\n");
   printf("Micro-step= %d  (%d steps/turn)\n\n",MICROSTEP, MICROSTEP * STEP_PER_REVOLUTION );


   // initialize pigpio
   gpioInitialise();

   // create exit handler
   atexit(bye);
   signal(SIGINT, MyCtrlC);


   //  memory map GPIO. This way I could use GPIO in Parallel  mode.
   setup_io();

   //  Create Stepper table
   BuildCoilTable();

//   printf("GPIO Mask : %08lX\n", StepperMask);
//   for(loop=0;loop< CoilTableSize; loop++)
//    printf("%d: %08lX %03d %03d\n", loop, StepperCoil[loop], StepperAPWM[loop],StepperBPWM[loop]);


   // set PWM

   gpioSetMode(PWM_A, PI_OUTPUT);
   gpioSetMode(PWM_B, PI_OUTPUT);
   gpioSetPWMfrequency(PWM_A, 100000);
   gpioSetPWMfrequency(PWM_B, 100000);
   gpioSetPWMrange(PWM_A,255);
   gpioSetPWMrange(PWM_B,255);


   // set GPIO OUTPUT

   INP_GPIO(STEP_A);
   OUT_GPIO(STEP_A);
   INP_GPIO(STEP_AN);
   OUT_GPIO(STEP_AN);
   INP_GPIO(STEP_B);
   OUT_GPIO(STEP_B);
   INP_GPIO(STEP_BN);
   OUT_GPIO(STEP_BN);

   // put all Coil to 0
   GPIO_CLR=StepperMask;



   int p;
   while(!exitFlag)
   {

     printf("Stepper position %ld.   Number of step to move (0=exit) ?",GetStepperPosition());

     if(scanf("%d",&p)==1)
      {
         if(p==0) break;
        MoveStepper(p);

        MoveStepper(0); // this will just wait until is done.
      }
   }

   usleep(100000);
   GPIO_CLR=StepperMask;

   return 0;
}
I'm using pigpio for the PWM. You will need to install it

http://abyz.co.uk/rpi/pigpio/download.html

And this is how you compile it

Code: Select all

gcc -o stepper stepper.c  -lm -lpthread -lpigpio -lrt

This is is a small video of the micro-step in action. The "stepperFull" application is full step and you will see that the vibration is strong enough to move to motor by itself.


https://dl.dropboxusercontent.com/s/zrk ... tepper.mp4

P.S. you could break the program if you enter 0 at the prompt or press ctrl-c when it is running.

B.T.W. Normally I use a sine wave with the SLA7026 from Allegro but with the L298 I had to use the square root of the sine wave if I want the equally space angle.

Daniel
Last edited by danjperron on Fri Mar 01, 2019 3:52 pm, edited 1 time in total.

User avatar
joan
Posts: 14393
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Micro-Stepping with the Raspberry PI using L298 driver

Tue Sep 09, 2014 8:20 am

I was wondering if there is an optimum number of microsteps to use? The fewer the better to maintain torque?

A piscope webm view.

I'm not sure if you are using the gpio macros for performance reasons or to get parallel access to the gpios. If it's the latter there are functions to operate on the gpios in parallel such as http://abyz.co.uk/rpi/pigpio/cif.html#g ... 0_31_Clear.

danjperron
Posts: 3411
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Micro-Stepping with the Raspberry PI using L298 driver

Tue Sep 09, 2014 11:26 am

Thanks Joan,

I didn't know about those functions.

I remove the GPIO memory access and use your functions for GPIO in parallel. This will simplify the code.

Code: Select all

/************

Raspberry Pi Micro-stepping demo
using GPIO connected to a L298 I.C.

Author: Daniel Perron
Date:   7 septembre 2014


version 1.01   9 septembre 2014
 -  removes memory access to run GPIO in parallel. Full usage of pigpio.

External library  :
   PIGPIO    http://abyz.co.uk/rpi/pigpio/download.html

How to compile :

gcc -o stepper stepper.c  -lm -lpthread -lpigpio -lrt


GPL License

This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*******************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sched.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <pthread.h>
#include <pigpio.h>
#include <signal.h>


// =========   GPIO  IO DEFINITION

#define STEP_A  17
#define STEP_AN 18 
#define STEP_B  22
#define STEP_BN 25


#define PWM_A  23
#define PWM_B  24

#define MICROSTEP   5

long  StepperDelay = 50000/MICROSTEP;

#define STEP_PER_REVOLUTION 200


// ===========   STEPPER  DEFINITION



#define MICROSTEP_PER_REVOLUTION (STEP_PER_REVOLUTION * MICROSTEP)
#define CoilTableSize  (4 * MICROSTEP)



unsigned long StepperCoil[CoilTableSize];
unsigned char StepperAPWM[CoilTableSize];
unsigned char StepperBPWM[CoilTableSize];

unsigned long StepperMask = (1<<STEP_A) | (1<<STEP_AN) | (1<<STEP_B) | ( 1<<STEP_BN);

volatile long StepperPosition = 0;
volatile long TargetPosition = 0;
volatile int  StepperReadyFlag = 1;

pthread_mutex_t StepperThread_mutex;
pthread_mutex_t Stepper_mutex;
pthread_t StepperThreadId;

// ============   FUNCTION

void set_max_priority(void) {
  struct sched_param sched;
  memset(&sched, 0, sizeof(sched));
  // Use FIFO scheduler with highest priority for the lowest chance of the kernel context switching.
  sched.sched_priority = sched_get_priority_max(SCHED_FIFO);
  sched_setscheduler(0, SCHED_FIFO, &sched);
}

void set_default_priority(void) {
  struct sched_param sched;
  memset(&sched, 0, sizeof(sched));
  // Go back to default scheduler with default 0 priority.
  sched.sched_priority = 0;
  sched_setscheduler(0, SCHED_OTHER, &sched);
}



// ======= stepper thread



void SetStepper(unsigned long  StepperMicroStep)
{
  int  index = StepperMicroStep % CoilTableSize;

  gpioWrite_Bits_0_31_Clear(StepperMask & ~ StepperCoil[index]);
  gpioWrite_Bits_0_31_Set(StepperCoil[index]);
  gpioPWM(PWM_A, StepperAPWM[index]);
  gpioPWM(PWM_B, StepperBPWM[index]);
/*  printf("Position = %d Index = %d  PWA:%d PWB:%d",StepperMicroStep,index,StepperAPWM[index],StepperBPWM[index]);
  if(StepperCoil[index] & (1 << STEP_A)) printf(" A+ ");
  if(StepperCoil[index] & (1 << STEP_AN)) printf(" A- ");
  if(StepperCoil[index] & (1 << STEP_B)) printf(" B+ ");
  if(StepperCoil[index] & (1 << STEP_BN)) printf(" B- ");
  printf("\n");
*/
}


int exitFlag=0;
int ThreadReturn;
void*  StepperThreadFunction(void  * arg )
{
  long ltemp;
  
  set_max_priority();

  while(!exitFlag)
  {

   // Lock  position
   pthread_mutex_lock(&Stepper_mutex);

   if(TargetPosition == StepperPosition)
     {
        StepperReadyFlag=1;
        pthread_mutex_unlock(&Stepper_mutex);
        break;
      }
   StepperReadyFlag=0;
   if(TargetPosition < StepperPosition)
      StepperPosition--;
    else
      StepperPosition++;
    ltemp = StepperPosition;
    pthread_mutex_unlock(&Stepper_mutex);
    SetStepper(ltemp);
    // Set Current Position
    usleep(StepperDelay);
   }

//   printf("Thread close \n");
   set_default_priority();
   pthread_mutex_unlock(&StepperThread_mutex);
   pthread_exit(&ThreadReturn);
  return NULL;
}

//  FUNCTION


long GetStepperPosition(void)
{
  long ltemp;
  pthread_mutex_lock(&Stepper_mutex);
    ltemp = StepperPosition;
  pthread_mutex_unlock(&Stepper_mutex);
  return ltemp;
}


void MoveStepper(long value)
{
  // Lock mutex to  create Thread
  pthread_mutex_lock(&StepperThread_mutex);

  // Set  new Target position
  TargetPosition += value;

   // create the thread to move stepper
    int err = pthread_create(&StepperThreadId, NULL, &StepperThreadFunction, NULL);
        if (err != 0)
           {
             printf("\ncan't create thread :[%s]", strerror(err));
             pthread_mutex_unlock(&StepperThread_mutex);
           }
}



void BuildCoilTable(void)
{
  int loop;

  // Set first coil
  for(loop=0;loop<(CoilTableSize/2);loop++)
     StepperCoil[loop]= 1 << STEP_A;
  for(;loop<CoilTableSize;loop++)
     StepperCoil[loop]= 1 << STEP_AN;

  // Set second coil
  for(loop=0;loop<(CoilTableSize/2);loop++)
     StepperCoil[(loop + CoilTableSize/4) % CoilTableSize]|= 1 << STEP_B;
  for(;loop<CoilTableSize;loop++)
     StepperCoil[(loop + CoilTableSize/4) % CoilTableSize]|= 1 << STEP_BN;


   // Set Power to coil
   for(loop=0;loop<CoilTableSize;loop++)
    {
      unsigned char _temp;
      double sinV = fabs(sin(M_PI * loop / (CoilTableSize/2.0)));
      _temp= (unsigned char) floor( 255.0 * sqrt(sinV));
      StepperAPWM[loop]= _temp;
      StepperBPWM[(loop + MICROSTEP) % CoilTableSize]= _temp;
    }
}



// exit handler.  Just kill the thread 
void   bye(void)
     {
       exitFlag=1;
       puts ("\nGoodbye, cruel world....");
       usleep(100000);
     }

// Control-C handler.  Somehow the pigpio unhandle it properly
void MyCtrlC(int sig)
{
  exitFlag=1;
}


int main(void)
{
   int loop;

   printf("Raspberry Pi micro-stepping demo with L298 driver\n");
   printf("V1.01 (c) Daniel Perron Sept.2014\n");
   printf("Micro-step= %d  (%d steps/turn)\n\n",MICROSTEP, MICROSTEP * STEP_PER_REVOLUTION );


   // create exit handler
   atexit(bye);
   signal(SIGINT, MyCtrlC);


   gpioInitialise();
   //  Create Stepper table
   BuildCoilTable();

//   printf("GPIO Mask : %08lX\n", StepperMask);
//   for(loop=0;loop< CoilTableSize; loop++)
//    printf("%d: %08lX %03d %03d\n", loop, StepperCoil[loop], StepperAPWM[loop],StepperBPWM[loop]);



   // set GPIO OUTPUT

   gpioSetMode(STEP_A, PI_OUTPUT);
   gpioSetMode(STEP_AN, PI_OUTPUT);
   gpioSetMode(STEP_B, PI_OUTPUT);
   gpioSetMode(STEP_BN, PI_OUTPUT);
   gpioSetMode(PWM_A, PI_OUTPUT);
   gpioSetMode(PWM_B, PI_OUTPUT);


   // set PWM

   gpioSetMode(PWM_A, PI_OUTPUT);
   gpioSetMode(PWM_B, PI_OUTPUT);
   gpioSetPWMfrequency(PWM_A, 1000000);
   gpioSetPWMfrequency(PWM_B, 1000000);
   gpioSetPWMrange(PWM_A,255);
   gpioSetPWMrange(PWM_B,255);



   // put all Coil to 0
   gpioWrite_Bits_0_31_Clear(StepperMask);


   int p;
   while(!exitFlag)
   {

     printf("Stepper position %ld.   Number of step to move (0=exit) ?",GetStepperPosition());

     if(scanf("%d",&p)==1)
      {
         if(p==0) break;
        MoveStepper(p);

        MoveStepper(0); // this will just wait until is done.
      }
   }

   usleep(100000);

   // put all Coil to 0
   gpioWrite_Bits_0_31_Clear(StepperMask);


   return 0;
}

Not really the torque is there, just increase the voltage . The things is vibration reduction. This is why I prefer the SLA7026 since you are able to control the current with the reference voltage. if you want to control the torque , you could add a Power Factor on the PWM.

ex : _PWM_A = PWM_A * (Power Factor / 100).
_PWM_B = PWM_B * (Power Factor / 100)

It is a good idea to reduce the power when the motor stop.

But The simplicity of the L298 make it easy to do.

I think that 10 micro-step is almost the limit. Maybe if I change the PWM range to an higher value, it will help.


B.T.W. I forgot to mention that you need to ground the centre tap of the coil. It works on unipolar stepper.

Daniel

danjperron
Posts: 3411
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Micro-Stepping with the Raspberry PI using L298 driver

Wed Sep 10, 2014 1:01 pm

This is a simple version in Python3


The source class MStepper.py

Code: Select all

#!/usr/bin/env python3

import time
import pigpio
import numpy
import math

class MStepper:

  def __init__(self,MicroStep):
    self.CoilA = 17
    self.CoilB = 22
    self.CoilC = 18
    self.CoilD = 25
    self.PwmAC = 23
    self.PwmBD = 24
    self.MicroStep = int(MicroStep)
    self.delay = 0.01
    self.Position = 0
    self.gpioMask = 0
    self.PwmRange    = 1000
    self.Flag = False
    self.BuildMicroStepTable()

  def BuildMicroStepTable(self):
    self.TableSize = int(self.MicroStep * 4)
    self.coilTable = numpy.zeros(self.TableSize, dtype = numpy.uint32)
    self.pwmACTable = numpy.zeros(self.TableSize, dtype = numpy.int16)
    self.pwmBDTable = numpy.zeros(self.TableSize, dtype = numpy.int16)
    #calculate CoilTable for gpio
    HalfSize = int(self.TableSize/2)
    for i in range(HalfSize):
      self.coilTable[i] = 1 << self.CoilA
    for i in range(HalfSize,self.TableSize):
      self.coilTable[i] = 1 << self.CoilC
    for i in range(HalfSize):
      self.coilTable[i+self.MicroStep]= self.coilTable[i+self.MicroStep] | (1 << self.CoilB)
    for i in range(HalfSize, self.TableSize):
      self.coilTable[(i+self.MicroStep) % self.TableSize]= self.coilTable[(i+self.MicroStep) % self.TableSize] | (1 << self.CoilD)
    # calculate PWM
    for i in range(self.TableSize):
      PValue =  math.sqrt(math.fabs(math.sin(math.pi * i / (self.TableSize / 2.0))))
      self.pwmACTable[i]= math.floor(self.PwmRange * PValue)
      self.pwmBDTable[(i + self.MicroStep) % self.TableSize]= self.pwmACTable[i]

  def setGPIO(self):
    #set GPIO OUTPUT
    self.gpioMask = 1<<self.CoilA | 1<<self.CoilB | 1<<self.CoilC | 1<<self.CoilD
    pigpio.set_mode(self.CoilA,pigpio.OUTPUT)
    pigpio.set_mode(self.CoilB,pigpio.OUTPUT)
    pigpio.set_mode(self.CoilC,pigpio.OUTPUT)
    pigpio.set_mode(self.CoilD,pigpio.OUTPUT)
    pigpio.set_mode(self.PwmAC,pigpio.OUTPUT)
    pigpio.set_mode(self.PwmBD,pigpio.OUTPUT)
    #No power on coil
    pigpio.clear_bank_1(self.gpioMask)

    pigpio.set_PWM_frequency(self.PwmAC,1000000)
    pigpio.set_PWM_frequency(self.PwmBD,1000000)
    pigpio.set_PWM_range(self.PwmAC,self.PwmRange)
    pigpio.set_PWM_range(self.PwmBD,self.PwmRange)

    self.BuildMicroStepTable()
    self.Flag= True

  def setStepper(self,position):
     if(self.Flag):
       #set gpio
       index = position % self.TableSize
       setmask = self.coilTable[index]
       pigpio.clear_bank_1(self.gpioMask & ~setmask)
       pigpio.set_bank_1(setmask)
       #set PWM
       pigpio.set_PWM_dutycycle(self.PwmAC, self.pwmACTable[index])
       pigpio.set_PWM_dutycycle(self.PwmBD, self.pwmBDTable[index])
       self.Position= position

  def stop(self):
       pigpio.clear_bank_1(self.gpioMask)
       #set PWM
       pigpio.set_PWM_dutycycle(self.PwmAC,0)
       pigpio.set_PWM_dutycycle(self.PwmBD,0)


  def moveTo(self, Target):
    if Target == self.Position :
      return
    if self.Position < Target:
      direction=1
    else:
      direction=(-1)
    for i in range(self.Position,Target, direction) :
      self.setStepper(i)
      time.sleep(self.delay)

  def move(self, Target):
    Target = Target + self.Position
    self.moveTo(Target)
and mtest.py the demo program

Code: Select all

#!/usr/bin/env python3

import pigpio
import MStepper
import time

pigpio.start()

#create a 5 micro-step stepper
m1 = MStepper.MStepper(5)

#define your own GPIO pin

m1.CoilA = 17
m1.CoilB = 22
m1.CoilC = 18
m1.CoilD = 25
m1.PwmAC = 23
m1.PwmBD = 24

#set GPIO and calculate GPIO Table
m1.setGPIO()

#Activate coil and set position
m1.setStepper(0)
time.sleep(1.0)


#ok move half turn
m1.move(500)

#ok move to a specific position
#this will move full turn backward
#and move faster
m1.delay=0.001
m1.moveTo(0)

#ok let's do 10 micro-step
m1.MicroStep=10
m1.setGPIO()

#and do half turn

m1.delay=0.01
m1.moveTo(2000)

#de-activate coil
m1.stop()

pigpio.stop()


you need to start pigpio first

Code: Select all

sudo pigpiod
python3 mtest.py

Note. if you find that when you paste code from the forum and they don't start at the first column .
You could use the command cut to remove the spacing. Like here. when I cut and paste, I got 5 spaces added at every lines.

To to get rid of them I use something like

Code: Select all

 mv MStepper.py bad.py; cat bad.py | cut -c5- > MStepper.py; rm bad.py


Daniel

slawekw79
Posts: 31
Joined: Tue Dec 30, 2014 3:36 pm

Re: Micro-Stepping with the Raspberry PI using L298 driver

Wed Jan 21, 2015 4:39 pm

HI
I try run your Python3 code but i have a problem:

Code: Select all

pi@raspberrypi ~ $ sudo pigpiod
pi@raspberrypi ~ $ python3 mtest.py
Traceback (most recent call last):
  File "mtest.py", line 7, in <module>
    pigpio.start()
AttributeError: 'module' object has no attribute 'start'
pi@raspberrypi ~ $ 
I have installed pigpio tested, but i'm not sure where is problem

danjperron
Posts: 3411
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Micro-Stepping with the Raspberry PI using L298 driver

Wed Jan 21, 2015 4:51 pm

The new release of pigpio is different


instead of pigpio.start


you need to create an object


pi = pigpio.pi()


I will revised my code tonight since mstepper .py need to be modified also.

Daniel

danjperron
Posts: 3411
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Micro-Stepping with the Raspberry PI using L298 driver

Wed Jan 21, 2015 6:17 pm

I modified the code but can't test it . I don't have a Pi near me.


MStepper.py
https://dl.dropboxusercontent.com/s/zgn ... Stepper.py

mtest.py
https://dl.dropboxusercontent.com/s/nlb ... e/mtest.py

Just tell me if it is working .

Daniel
Last edited by danjperron on Fri Mar 01, 2019 3:55 pm, edited 1 time in total.

slawekw79
Posts: 31
Joined: Tue Dec 30, 2014 3:36 pm

Re: Micro-Stepping with the Raspberry PI using L298 driver

Wed Jan 21, 2015 7:30 pm

Yes thank you it's working.
Now I see I have another issue with stepper motor or L298n, because is moving but not smooth i had this issue with other codes and i suspect one of coils in motor is not perfect or H-bridge is defective.
Thanks again for your great work.

slawekw79
Posts: 31
Joined: Tue Dec 30, 2014 3:36 pm

Re: Micro-Stepping with the Raspberry PI using L298 driver

Thu Jan 22, 2015 4:08 pm

Hi
one technical question, in your code mtest.py

Code: Select all

#ok move half turn
m1.move(500)
this (500) it's mean 500 microsteps?
if I change this part to:

Code: Select all

m1.delay=1
m1.move(1000)
my stepper motor will make one full turn with 1 sec delay between each microstep?

danjperron
Posts: 3411
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Micro-Stepping with the Raspberry PI using L298 driver

Thu Jan 22, 2015 4:56 pm

Is your stepper 1.8 degree per step?

If yes a move(1000) will be a full rotation with each micro-step on every
~second.

If you want better precision use the real clock for second stepping.

If you want to mimic a time clock , convert the time clock to unit of 1000 and use moveTo(). Be carefull about transistion from 1000 to 0.

You could create time pulse and use a callback to move the stepper.

B.T.W. you could change the micro-step.

slawekw79
Posts: 31
Joined: Tue Dec 30, 2014 3:36 pm

Re: Micro-Stepping with the Raspberry PI using L298 driver

Thu Jan 22, 2015 5:50 pm

Yes my motor is 1.8 deg. I need move a motor 1 rev in 14 minutes and 21,64 sec, and time between "steps" or "micro-steps" maximum 0.5 sec. I need such a precision because i will use my project to astrophotography with my DSLR and 400mm lens. Is a quite big lens and over 0.5 sec i see star movement on picture.
Thank you for your help

danjperron
Posts: 3411
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Micro-Stepping with the Raspberry PI using L298 driver

Thu Jan 22, 2015 5:58 pm

Use a precision gear reducer to remove the stepper vibration as much possible. This way you could increase the number of step and try to generate the pulse via a crystal. It is possible to use the build in oscillator of the Pi to have an accurate clock.

Return to “Automation, sensing and robotics”