Skip to content

Commit

Permalink
Merge branch 'V0.7---Beta'
Browse files Browse the repository at this point in the history
  • Loading branch information
MicroBahner committed Jan 20, 2017
2 parents 78a054c + d58285a commit 9f44b9a
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 32 deletions.
Binary file removed MobaTools-06.pdf
Binary file not shown.
Binary file added MobaTools-07 -en.pdf
Binary file not shown.
Binary file added MobaTools-07.pdf
Binary file not shown.
121 changes: 94 additions & 27 deletions MobaTools.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@

/*
MobaTools V0.6
MobaTools V0.7
(C) 08-2015 fpm fpm@mnet-online.de
History:
V0.7 Allow nested Interrupts with the servos. This allows more precise other
interrupts e.g. for NmraDCC Library.
A4988 stepper driver IC is supported (needs only 2 ports: step and direction)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
Expand Down Expand Up @@ -81,8 +84,8 @@
#define SET_TP4
#define CLR_TP4
#endif
static char dbgbuf[80];
#define DebugPrint( ... ) sprintf( dbgbuf, "DBG: " __VA_ARGS__ ); Serial.println( dbgbuf );
#define DB_PRINT( x, ... ) { sprintf_P( dbgBuf, PSTR( x ), __VA_ARGS__ ) ; Serial.println( dbgBuf ); }
static char dbgBuf[80];
#else
#define MODE_TP1
#define SET_TP1
Expand All @@ -97,7 +100,7 @@
#define SET_TP4
#define CLR_TP4

#define DebugPrint( ... ) ;
#define DB_PRINT ;
#endif


Expand Down Expand Up @@ -176,6 +179,14 @@ ISR ( TIMER1_COMPB_vect)
CLR_TP2;
for ( i=0; i<stepperCount; i++ ) {
// für maximal 4 Motore
if ( stepperData[i].output == A4988_PINS ) {
// reset step pulse - pulse is max one cycle lenght
#ifdef FAST_PORTWRT
*stepperData[i].portPins[0].Adr &= ~stepperData[i].portPins[0].Mask;
#else
digitalWrite( stepperData[i].pins[0], LOW );
#endif
}
if ( stepperData[i].activ && stepperData[i].stepCnt > 0 ) {
// only active motors
stepperData[i].cycCnt+=cyclesLastIRQ;
Expand Down Expand Up @@ -237,6 +248,30 @@ ISR ( TIMER1_COMPB_vect)
}
stepperData[i].lastPattern = stepPattern[ stepperData[i].patternIx ];
break;
case A4988_PINS : // output step-pulse and direction
// direction first
if ( stepperData[i].patternIxInc > 0 ) {
// turn forward
#ifdef FAST_PORTWRT
*stepperData[i].portPins[1].Adr |= stepperData[i].portPins[1].Mask;
#else
digitalWrite( stepperData[i].pins[1], HIGH );
#endif
} else {
// turn backwards
#ifdef FAST_PORTWRT
*stepperData[i].portPins[1].Adr &= ~stepperData[i].portPins[1].Mask;
#else
digitalWrite( stepperData[i].pins[1], LOW );
#endif
}
// Set step pulse ( will be resettet in next IRQ )
#ifdef FAST_PORTWRT
*stepperData[i].portPins[0].Adr |= stepperData[i].portPins[0].Mask;
#else
digitalWrite( stepperData[i].pins[0], HIGH );
#endif

default:
// should never be reached
break;
Expand Down Expand Up @@ -344,9 +379,11 @@ ISR ( TIMER1_COMPA_vect)
#else // create overlapping servo pulses
// Positions of servopulses within 20ms cycle are variable, max 2 pulses at the same time
// 27.9.15 with variable overlap, depending on length of next pulse: 16 Servos
// 2.1.16 Enable interrupts after timecritical path (e.g. starting/stopping servo pulses)
// so other timecritical tasks can interrupt (nested interrupts)
static bool searchNextPulse() {
while ( pulseIx < servoCount && servoData[pulseIx].soll < 0 ) {
SET_TP2;
//SET_TP2;
pulseIx++;
CLR_TP2;
}
Expand Down Expand Up @@ -389,9 +426,10 @@ ISR ( TIMER1_COMPA_vect)
// next starttime must behind actual timervalue and endtime of next pulse must
// lay after endtime of runningpuls + safetymargin (it may be necessary to start
// another pulse between these 2 ends)
word tmpTCNT1 = TCNT1 + MARGINTICS/2;
sei();
CLR_TP3 ;
OCR1A = max ( ((long)activePulseOff + (long) MARGINTICS - (long) nextPulseLength), ( TCNT1 + MARGINTICS/2 ) );
SET_TP3; // Oszimessung Dauer der ISR-Routine
OCR1A = max ( ((long)activePulseOff + (long) MARGINTICS - (long) nextPulseLength), ( tmpTCNT1 ) );
} else {
// we are at the end, no need to start another pulse in this cycle
if ( activePulseOff ) {
Expand Down Expand Up @@ -425,6 +463,8 @@ ISR ( TIMER1_COMPA_vect)
digitalWrite( servoData[nextPulseIx].pin, HIGH );
#endif
}
sei(); // the following isn't time critical, so allow nested interrupts
SET_TP3;
// the 'nextPulse' we have started now, is from now on the 'activePulse', the running activPulse is now the
// pulse to stop next.
stopPulseIx = activePulseIx; // because there was a 'nextPulse' there is also an 'activPulse' which is the next to stop
Expand All @@ -448,16 +488,17 @@ ISR ( TIMER1_COMPA_vect)
digitalWrite( servoData[pulseIx].pin, HIGH );
#endif
}
word tmpTCNT1 = TCNT1;
sei(); // the following isn't time critical, so allow nested interrupts
SET_TP3;
// look for second pulse
pulseIx++;
if ( searchNextPulse() ) {
CLR_TP1;
// there is a second pulse - this is the 'nextPulse'
nextPulseLength = servoData[pulseIx].ist;
nextPulseIx = pulseIx++;
// set Starttime for 2. pulse in sequence
OCR1A = max ( ((long)activePulseOff + (long) MARGINTICS - (long) nextPulseLength), ( TCNT1 + MARGINTICS/2 ) );
SET_TP1;
OCR1A = max ( ((long)activePulseOff + (long) MARGINTICS - (long) nextPulseLength), ( tmpTCNT1 + MARGINTICS/2 ) );
} else {
// no next pulse, there is only one pulse
OCR1A = activePulseOff;
Expand All @@ -484,7 +525,7 @@ ISR ( TIMER1_COMPA_vect)
IrqType = POFF;
}
}
CLR_TP1; // Oszimessung Dauer der ISR-Routine
CLR_TP1; CLR_TP3; // Oszimessung Dauer der ISR-Routine

} //end of 'pulse ON'
}
Expand Down Expand Up @@ -562,7 +603,7 @@ void Stepper4::initialize ( int steps360, uint8_t mode, uint8_t minStepTime ) {
// create new instance
stepperIx = stepperCount++ ;
stepsRev = steps360; // number of steps for full rotation in fullstep mode
if ( mode != FULLSTEP ) mode = HALFSTEP;
if ( mode != FULLSTEP && mode != A4988 ) mode = HALFSTEP;
// initialize data for interrupts
stepperData[stepperIx].stepCnt = 0; // don't move
stepMode = mode;
Expand All @@ -589,6 +630,16 @@ long Stepper4::getSFZ() {
}

// public functions -------------------
uint8_t Stepper4::attach( byte stepP, byte dirP ) {
// step motor driver A4988 is used
byte pins[2];
if ( stepMode != A4988 ) return 0; // false mode
DB_PRINT( "Attach4988, S=%d, D=%d", stepP, dirP );

pins[0] = stepP;
pins[1] = dirP;
return Stepper4::attach( A4988_PINS, pins );
}
uint8_t Stepper4::attach( byte pin1, byte pin2, byte pin3, byte pin4 ) {
byte pins[4];
pins[0] = pin1;
Expand All @@ -598,11 +649,11 @@ uint8_t Stepper4::attach( byte pin1, byte pin2, byte pin3, byte pin4 ) {
return Stepper4::attach( SINGLE_PINS, pins );
}
uint8_t Stepper4::attach(byte outArg) {
return Stepper4::attach( outArg, NULL );
return Stepper4::attach( outArg, (byte *)NULL );
}

uint8_t Stepper4::attach( byte outArg, byte pins[] ) {
// outArg must be one of PIN8_11 ... SPI_4 or SINGLE_PINS
// outArg must be one of PIN8_11 ... SPI_4 or SINGLE_PINS, A4988_PINS
if ( stepMode == NOSTEP ) return 0; // Invalid object
uint8_t attachOK = true;
switch ( outArg ) {
Expand Down Expand Up @@ -654,7 +705,23 @@ uint8_t Stepper4::attach( byte outArg, byte pins[] ) {
digitalWrite( pins[i], LOW );
}
break;
default:
case A4988_PINS:
// 2 single output pins (step and direction) - as yet there is no check if they are allowed!
for ( byte i = 0; i<2; i++ ) {
#ifdef FAST_PORTWRT
// compute portadress and bitnumber
stepperData[stepperIx].portPins[i].Adr = (byte *) pgm_read_word_near(&port_to_output_PGM[pgm_read_byte_near(&digital_pin_to_port_PGM[ pins[i]])]);
stepperData[stepperIx].portPins[i].Mask = pgm_read_byte_near(&digital_pin_to_bit_mask_PGM[pins[i]]);
#else // store pins directly
stepperData[stepperIx].pins[i] = pins[i];
#endif
stepMode = HALFSTEP; // There are no real stepmodes in A4988 - mode
stepperData[stepperIx].patternIxInc = 1; // defines direction
pinMode( pins[i], OUTPUT );
digitalWrite( pins[i], LOW );
}
break;
default:
// invalid Arg
attachOK = false;
}
Expand All @@ -669,7 +736,7 @@ uint8_t Stepper4::attach( byte outArg, byte pins[] ) {
TIMSK1 |= _BV(OCIE1B) ;
#endif
}

DB_PRINT( "attach: output=%d, attachOK=%d", stepperData[stepperIx].output, attachOK );
//Serial.print( "Attach Stepper, Ix= "); Serial.println( stepperIx );
return attachOK;
}
Expand Down Expand Up @@ -703,7 +770,7 @@ void Stepper4::setZero() {

void Stepper4::write(long angleArg ) {
// set next position as angle, measured from last setZero() - point
//DebugPrint( "Pin: " ,pin);DebugPrint("Wert: ", angleArg);
DB_PRINT("write: %d", angleArg);
Stepper4::write( angleArg, 1 );
}

Expand All @@ -714,7 +781,7 @@ void Stepper4::write( long angleArg, byte fact ) {
bool negative;
int angel2steps;
negative = ( angleArg < 0 ) ;
DebugPrint( "angleArg: ",angleArg ); //DebugPrint( " getSFZ: ", getSFZ() );
DB_PRINT( "angleArg: %d",angleArg ); //DB_PRINT( " getSFZ: ", getSFZ() );
//Serial.print( "Write: " ); Serial.println( angleArg );
angel2steps = ( (abs(angleArg) * (long)stepsRev*10) / ( 360L * fact) +5) /10 ;
if ( negative ) angel2steps = -angel2steps;
Expand Down Expand Up @@ -752,7 +819,7 @@ void Stepper4::doSteps( long stepValue ) {
if ( stepMode == NOSTEP ) return ; // Invalid object
//Serial.print( "doSteps: " ); Serial.println( stepValue );
stepsToMove = stepValue;
//DebugPrintln( " stepsToMove: ",stepsToMove );
DB_PRINT( " stepsToMove: %d ",stepsToMove );
if ( stepValue > 0 ) stepperData[stepperIx].patternIxInc = abs( stepperData[stepperIx].patternIxInc );
else stepperData[stepperIx].patternIxInc = -abs( stepperData[stepperIx].patternIxInc );
uint8_t oldSREG = SREG;
Expand Down Expand Up @@ -858,7 +925,7 @@ uint8_t Servo8::attach( int pinArg, int pmin, int pmax, bool autoOff ) {
// set pulselength for angle 0 and 180
if ( pmin >= MINPULSEWIDTH && pmin <= MAXPULSEWIDTH) min16 = pmin/16;
if ( pmax >= MINPULSEWIDTH && pmax <= MAXPULSEWIDTH ) max16 = pmax/16;
DebugPrint( "pin: %d, pmin:%d pmax%d autoOff=%d, min16=%d, max16=%d", pinArg, pmin, pmax, autoOff, min16, max16);
DB_PRINT( "pin: %d, pmin:%d pmax%d autoOff=%d, min16=%d, max16=%d", pinArg, pmin, pmax, autoOff, min16, max16);

// intialize objectspecific global data
lastPos = 3000 ; // initalize to middle position
Expand All @@ -872,8 +939,8 @@ uint8_t Servo8::attach( int pinArg, int pmin, int pmax, bool autoOff ) {
// compute portaddress and bitmask related to pin number
servoData[servoIndex].portAdr = (byte *) pgm_read_word_near(&port_to_output_PGM[pgm_read_byte_near(&digital_pin_to_port_PGM[ pinArg])]);
servoData[servoIndex].bitMask = pgm_read_byte_near(&digital_pin_to_bit_mask_PGM[pinArg]);
DebugPrint( "Idx: %d Portadr: 0x%x, Bitmsk: 0x%x", servoIndex, servoData[servoIndex].portAdr, servoData[servoIndex].bitMask );
DebugPrint( "Stack=0x%04x, &sIx=0x%04x", ((SPH&0x7)<<8)|SPL, &servoIndex );
DB_PRINT( "Idx: %d Portadr: 0x%x, Bitmsk: 0x%x", servoIndex, servoData[servoIndex].portAdr, servoData[servoIndex].bitMask );
DB_PRINT( "Stack=0x%04x, &sIx=0x%04x", ((SPH&0x7)<<8)|SPL, &servoIndex );
#endif
pin = pinArg;
angle = NO_ANGLE;
Expand Down Expand Up @@ -905,10 +972,10 @@ void Servo8::write(int angleArg)
// values between 0 and 180 are interpreted as degrees,
// values between MINPULSEWIDTH and MAXPULSEWIDTH are interpreted as microseconds
static int newpos;
DebugPrint( "Write: angleArg=%d, Soll=%d", angleArg, servoData[servoIndex].soll );
DB_PRINT( "Write: angleArg=%d, Soll=%d", angleArg, servoData[servoIndex].soll );
if ( pin > 0 ) { // only if servo is attached
//Serial.print( "Pin:" );Serial.print(pin);Serial.print("Wert:");Serial.println(angleArg);
DebugPrint( "Stack=0x%04x, &sIx=0x%04x", ((SPH&0x7)<<8)|SPL, &servoIndex );
DB_PRINT( "Stack=0x%04x, &sIx=0x%04x", ((SPH&0x7)<<8)|SPL, &servoIndex );
if ( angleArg < 0) angleArg = 0;
if ( angleArg <= 180) {
// pulse width as degrees
Expand All @@ -935,7 +1002,7 @@ void Servo8::write(int angleArg)
cli();
servoData[servoIndex].soll= newpos ; // .ist - value is still -1 (invalid) -> will jump to .soll immediately
SREG = oldSREG;
DebugPrint( "FirstWrite: Ix=%d,%d Soll=%d", servoIndex, this->servoIndex, servoData[servoIndex].soll );
DB_PRINT( "FirstWrite: Ix=%d,%d Soll=%d", servoIndex, this->servoIndex, servoData[servoIndex].soll );

}
else if ( newpos != servoData[servoIndex].soll ) {
Expand All @@ -945,7 +1012,7 @@ void Servo8::write(int angleArg)
cli();
servoData[servoIndex].soll= newpos ;
SREG = oldSREG;
DebugPrint( "NextWrite: Ix= %d,%d Soll=%d, On=%d", servoIndex, this->servoIndex, servoData[servoIndex].soll, servoData[servoIndex].on );
DB_PRINT( "NextWrite: Ix= %d,%d Soll=%d, On=%d", servoIndex, this->servoIndex, servoData[servoIndex].soll, servoData[servoIndex].on );
}
}
}
Expand Down
24 changes: 20 additions & 4 deletions MobaTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,22 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

/* 02-11-16 Updating Stepper driver:
- stepper motor can be connected ba means of a4988 stepper motor driver IC
this uses only 2 pins: STEP and DIRECTION
*/
#include <inttypes.h>
#include <Arduino.h>

#ifndef __AVR_MEGA__
#error "Only AVR AtMega processors are supported"
#endif

#define Servo2 Servo8 // Kompatibilität zu Version 01 und 02
//defines used in user programs
#define HALFSTEP 1
#define FULLSTEP 2
#define A4988 3 // using motordriver A4988
#define NOSTEP 0 // invalid-flag

#define NO_OUTPUT 0
Expand All @@ -41,6 +50,7 @@
#define SPI_3 5
#define SPI_4 6
#define SINGLE_PINS 7
#define A4988_PINS 8

// for formatted printing to Serial( just like fprintf )
// you need to define txtbuf with proper length to use this
Expand Down Expand Up @@ -93,12 +103,15 @@ typedef struct { // portaddress and bitmask for direkt pin set/reset
typedef struct {
volatile long stepCnt; // nmbr of steps to take
volatile int8_t patternIx; // Pattern-Index of actual Step (0-7)
int8_t patternIxInc; // halfstep: +/-1, fullstep: +/-2, sign defines direction
int8_t patternIxInc; // halfstep: +/-1, fullstep: +/-2, A4988 +3/-3/
// sign defines direction
uint16_t cycSteps; // nbr of IRQ cycles per step ( speed of motor )
uint16_t rCycSteps; // nbr of IRQ cycles per step during start/stop ramp
uint16_t rStepDec; // count of steps until decrementing cycle ( during start ramp )
volatile uint16_t cycCnt; // counting cycles until cycStep
volatile long stepsFromZero; // distance from last reference point ( always as steps in HALFSTEP mode )
// in FULLSTEP mode this is twice the real step number
byte output :6 ; // PORTB(pin8-11), PORTD (pin4-7), SPI0,SPI1,SPI2,SPI3
byte output :6 ; // PORTB(pin8-11), PORTD (pin4-7), SPI0,SPI1,SPI2,SPI3, SINGLE_PINS, A4988_PINS
byte activ :1;
byte endless :1; // turn endless
#ifdef FAST_PORTWRT
Expand Down Expand Up @@ -126,7 +139,7 @@ typedef union { // used output channels as bit und byte
typedef struct {
int soll = -1; // Position, die der Servo anfahren soll ( in Tics )
volatile int ist; // Position, die der Servo derzeit einnimt ( in Tics )
byte inc; // Schrittweite je Zyklus um Ist an Soll anzugleichen
int inc; // Schrittweite je Zyklus um Ist an Soll anzugleichen
byte offcnt; // counter to switch off pulses if length doesn't change
#ifdef FAST_PORTWRT
byte* portAdr; // port adress related to pin number
Expand Down Expand Up @@ -155,7 +168,9 @@ class Stepper4
int stepsRev; // steps per full rotation
int stepsToMove; // from last point
uint8_t stepMode; // FULLSTEP or HALFSTEP
uint8_t minCycSteps; // minimum time between 2 steps
uint8_t minCycSteps; // minimum time between 2 steps without ramp
// ramp starts with this speed if wanted speed ist faster
uint8_t minrCycSteps; // absolute minimum time between 2 steps even with ramp
static outUsed_t outputsUsed;
long getSFZ(); // get step-distance from last reference point
void initialize(int,uint8_t,uint8_t);
Expand All @@ -166,6 +181,7 @@ class Stepper4
Stepper4(int steps, uint8_t mode, uint8_t minStepTime ); // min StepTim in ms

uint8_t attach( byte,byte,byte,byte); //single pins definition for output
uint8_t attach( byte stepP, byte dirP); // Port for step and direction in A4988 mode
uint8_t attach(byte outArg); // stepMode defaults to halfstep
uint8_t attach(byte outArg, byte* );
// returns 0 on failure
Expand Down
Binary file added ToDo.rtf
Binary file not shown.
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=MobaTools
version=0.6.0
version=0.7.0
author=MicroBahner
maintainer=
sentence=Special functions for model railroaders, for arduino uno, mini, nano, micro and mega
Expand Down

0 comments on commit 9f44b9a

Please sign in to comment.