| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- #ifndef MOBATOOLS_H
- #define MOBATOOLS_H
- /*
- MobaTools.h - a library for model railroaders
- Author: fpm, fpm@mnet-mail.de
- Copyright (c) 2017 All right reserved.
- MobaTools V1.0
- (C) 11-2017 fpm fpm@mnet-online.de
-
- History:
- V1.0 11-2017 Use of Timer 3 if available ( on AtMega32u4 and AtMega2560 )
- V0.9 03-2017
- Better resolution for the 'speed' servo-paramter (programm starts in compatibility mode)
- outputs for softleds can be inverted
- MobaTools run on STM32F1 platform
-
- V0.8 02-2017
- Enable Softleds an all digital outputs
- V0.7 01-2017
- 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)
- Library to drive the Stepper Motor 28BYJ-48
- connected to SPI (MOSI,CLK,SS) Interface via shift register
- or 4 pins directly
-
- This library 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 2.1 of the License, or (at your option) any later version.
- This library 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
- Lesser General Public License for more details.
- You should have received a copy of the GNU General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
- /* 08-02-17 start of implementing STM32 support
- 03-02-17 / V0.8 Softleds now working on all digital outputs
- 02-11-16 / (V0.7) 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__
- #ifndef __STM32F1__
- #error "Only AVR AtMega or STM32F1 processors are supported"
- #endif
- #endif
- #ifdef __STM32F1__
- #include <libmaple/timer.h>
- #include <libmaple/spi.h>
- #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
- #ifdef __AVR_MEGA__
- #define PIN8_11 1
- #define PIN4_7 2
- #endif
- #define SPI_1 3
- #define SPI_2 4
- #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
- #define SerialPrintf( ... ) sprintf( txtbuf, __VA_ARGS__ ); Serial.print( txtbuf );
- //#define FIXED_POSITION_SERVO_PULSES
- ////////////////// END OF 'PUBLIC' PARAMETERS ////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // internal defines
- #ifdef __AVR_MEGA__
- // defines only for ATMega
- #define FAST_PORTWRT // if this is defined, ports are written directly in IRQ-Routines,
- // not with 'digitalWrite' functions
- #define TICS_PER_MICROSECOND (clockCyclesPerMicrosecond() / 8 ) // prescaler is 8 = 0.5us
-
- #elif defined __STM32F1__
- //defines only for STM32
- #define TICS_PER_MICROSECOND (clockCyclesPerMicrosecond() / 36 ) // prescaler is 36 = 0.5us
- #endif
- // defines for the stepper motor
- #define MAX_STEPPER 4 //
- #define MIN_STEPTIME 800 // minimum steptime between 2 steps
- #define CYCLETIME 200 // Irq-periode in us. Step time is an integer multiple
- // of this value
- #define CYCLETICS CYCLETIME*TICS_PER_MICROSECOND
- // defines for soft-leds
- #define MAX_LEDS 16 // Soft On/Off defined for compatibility reasons. There is no fixed limit anymore.
- // defines for servos
- #define MINPULSEWIDTH 700 // don't make it shorter
- #define MAXPULSEWIDTH 2300 // don't make it longer
- #ifdef FIXED_POSITION_SERVO_PULSES
- #define MAX_SERVOS 8
- #else
- #define OVLMARGIN 280 // Overlap margin ( Overlap is MINPULSEWIDTH - OVLMARGIN )
- #define OVL_TICS ( ( MINPULSEWIDTH - OVLMARGIN ) * TICS_PER_MICROSECOND )
- #define MARGINTICS ( OVLMARGIN * TICS_PER_MICROSECOND )
- #define MAX_SERVOS 16
- #endif
- #define MINPULSETICS MINPULSEWIDTH * TICS_PER_MICROSECOND
- #define MAXPULSETICS MAXPULSEWIDTH * TICS_PER_MICROSECOND
- #define OFF_COUNT 50 // if autoOff is set, a pulse is switched off, if it length does not change for
- // OFF_COUNT cycles ( = OFF_COUNT * 20ms )
- #define FIRST_PULSE 100 // first pulse starts 200 tics after timer overflow, so we do not compete
- // with overflow IRQ
- #define SPEED_RES 4 // All position values in tics are multiplied by this factor. This means, that one
- // 'Speed-tic' is 0,125 µs per 20ms cycle. This gives better resolution in defining the speed.
- // Only when computing the next interrupt time the values are divided by this value again to get
- // the real 'timer tics'
- ///////////////////////////////////////////////////////////////////////////////////////////////
- //
- typedef struct { // portaddress and bitmask for direkt pin set/reset
- uint8_t* Adr;
- uint8_t Mask;
- } portBits_t;
- /////////////////////////////////////////////////////////////////////////////////
- // global stepper data ( used in ISR )
- 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, 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
- uint8_t output :6 ; // PORTB(pin8-11), PORTD (pin4-7), SPI0,SPI1,SPI2,SPI3, SINGLE_PINS, A4988_PINS
- uint8_t activ :1;
- uint8_t endless :1; // turn endless
- #ifdef FAST_PORTWRT
- portBits_t portPins[4]; // Outputpins as Portaddress and Bitmask for faster writing
- #else
- uint8_t pins[4]; // Outputpins as Arduino numbers
- #endif
- uint8_t lastPattern; // only changed pins are updated ( is faster )
- } stepperData_t ;
- typedef union { // used output channels as bit and uint8_t
- struct {
- uint8_t pin8_11 :1;
- uint8_t pin4_7 :1;
- uint8_t spi1 :1;
- uint8_t spi2 :1;
- uint8_t spi3 :1;
- uint8_t spi4 :1;
- };
- uint8_t outputs;
-
- } outUsed_t;
- ////////////////////////////////////////////////////////////////////////////////////
- // global servo data ( used in ISR )
- typedef struct servoData_t {
- struct servoData_t* prevServoDataP;
- uint8_t servoIx :6 ; // Servo number
- uint8_t on :1 ; // True: create pulse
- uint8_t noAutoff :1; // don't switch pulses off automatically
- int soll; // Position, die der Servo anfahren soll ( in Tics ). -1: not initialized
- volatile int ist; // Position, die der Servo derzeit einnimt ( in Tics )
- int inc; // Schrittweite je Zyklus um Ist an Soll anzugleichen
- uint8_t offcnt; // counter to switch off pulses if length doesn't change
- #ifdef FAST_PORTWRT
- uint8_t* portAdr; // port adress related to pin number
- uint8_t bitMask; // bitmask related to pin number
- #endif
- uint8_t pin ; // pin
- } servoData_t ;
- //////////////////////////////////////////////////////////////////////////////////
- // global data for softleds ( used in ISR )
- // the PWM pulses are created together with stepper pulses
- //
- // table of pwm-steps for soft on/off in CYCLETIME units ( bulb simulation). The last value means pwm cycletime
- //14ms cycletime
- //const uint8_t iSteps[] = { 2, 3 , 4, 6, 8, 11, 14, 17, 21, 25, 30, 36, 43, 55, 70 };
- //16ms cycletime ( 60Hz )
- //const uint8_t iSteps[] = {2, 8 ,14 ,20, 25,30, 35, 39, 43, 47, 50, 53, 56, 58, 60, 62, 64,66,68,70,72,73,74,75,76,78,79,80 };
- const uint8_t iSteps[] = {9, 16 ,23 ,29, 35,41, 45, 49, 53, 56, 59, 62, 64, 66, 68, 70, 71,72,73,74,75,76,77,77,78,78,79,80 };
- // 20ms cycletime ( 50Hz )
- //const uint8_t iSteps[] = { 2,4, 6, 9, 12, 15, 20, 25, 30, 35, 40, 45,50,55,65,80,100 };
- #define LED_STEP_MAX (sizeof(iSteps) -1)
- #define LED_CYCLE_MAX (iSteps[LED_STEP_MAX])
- #define LED_PWMTIME (iSteps[LED_STEP_MAX] / 5) // PWM refreshrate in ms
- // todo: dies gilt nur bei einer CYCLETIME von 200us (derzeit default)
- typedef struct ledData_t { // global led values ( used in IRQ )
- struct ledData_t* nextLedDataP; // chaining the active Leds
- struct ledData_t** backLedDataPP; // adress of pointer, that points to this led (backwards reference)
- int8_t speed; // > 0 : steps per cycle ( more steps = more speed )
- // < 0 : cycles per step ( more cycles = less speed )
- // 0: led is inactive (not attached)
- // uint8_t invert=false; // false: ON ist HIGH, true: ON is LOW
- int8_t aStep; // actual step (brightness)
- int8_t aCycle; // actual cycle ( =length of PWM pule )
- int8_t stpCnt; // counter for PWM cycles on same step (for low speed)
- uint8_t actPulse; // PWM pulse is active
- uint8_t state; // actual state: steady or incementing/decrementing
- #define NOTATTACHED 0
- #define STATE_OFF 1 // using #defines here is a little bit faster in the ISR than enums
- #define STATE_ON 2
- #define ACTIVE 3 // state >= ACTIVE means active in ISR routine
- #define INCFAST 3
- #define DECFAST 4
- #define INCSLOW 5
- #define DECSLOW 6
- #define INCLIN 7
- #define DECLIN 8
-
- volatile uint8_t invFlg;
- #ifdef FAST_PORTWRT
- portBits_t portPin; // Outputpin as portaddress and bitmask for faster writing
- #else
- uint8_t pin; // Outputpins as Arduino numbers
- #endif
- } ledData_t;
- //////////////////////////////////////////////////////////////////////////////
- class Stepper4
- {
- private:
- uint8_t stepperIx; // Index in Structure
- int stepsRev; // steps per full rotation
- long stepsToMove; // from last point
- uint8_t stepMode; // FULLSTEP or HALFSTEP
- 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);
- public:
- Stepper4(int steps); // steps per 360 degree in FULLSTEP mode
- Stepper4(int steps, uint8_t mode );
- // mode means HALFSTEP or FULLSTEP
- Stepper4(int steps, uint8_t mode, uint8_t minStepTime ); // min StepTim in ms
-
- uint8_t attach( uint8_t,uint8_t,uint8_t,uint8_t); //single pins definition for output
- uint8_t attach( uint8_t stepP, uint8_t dirP); // Port for step and direction in A4988 mode
- uint8_t attach(uint8_t outArg); // stepMode defaults to halfstep
- uint8_t attach(uint8_t outArg, uint8_t* );
- // returns 0 on failure
- void detach(); // detach from output, motor will not move anymore
- void write(long angle); // specify the angle in degrees, mybe pos or neg. angle is
- // measured from last 'setZero' point
- void write(long angle, uint8_t factor); // factor specifies resolution of parameter angle
- // e.g. 10 means, 'angle' is angle in .1 degrees
- void writeSteps( long stepPos );// Go to position stepPos steps from zeropoint
- void setZero(); // actual position is set as 0 angle (zeropoint)
- int setSpeed(int rpm10 ); // Set movement speed, rpm*10
- void doSteps(long count); // rotate count steps. May be positive or negative
- // angle is updated internally, so the next call to 'write'
- // will move to the correct angle
- void rotate(int8_t direction ); // rotate endless until 'stop',
- void stop(); // stops moving immediately
- uint8_t moving(); // returns the remaining way to the angle last set with write() in
- // in percentage. '0' means, that the angle is reached
- // 255 means the motor is rotating endlessly
- long read(); // actual angle from zeropoint (setZero)
- long readSteps(); // actual distance to zeropoint in steps
- uint8_t attached();
- };
- ////////////////////////////////////////////////////////////////////////////////////////
- class Servo8
- {
- private:
- int16_t lastPos; // startingpoint of movement
- uint8_t pin;
- uint8_t angle; // in degrees
- uint8_t min16; // minimum pulse, 16uS units (default is 34)
- uint8_t max16; // maximum pulse, 16uS units, (default is 150)
- servoData_t servoData; // Servo data to be used in ISR
- public:
- Servo8();
- uint8_t attach(int pin); // attach to a pin, sets pinMode, returns 0 on failure, won't
- // position the servo until a subsequent write() happens
- uint8_t attach( int pin, bool autoOff ); // automatic switch off pulses with constant length
- uint8_t attach(int pin, int pos0, int pos180 ); // also sets position values (in us) for angele 0 and 180
- uint8_t attach(int pin, int pos0, int pos180, bool autoOff );
- void detach();
- void write(int); // specify the angle in degrees, 0 to 180. Values obove 180 are interpreted
- // as microseconds, limited to MaximumPulse and MinimumPulse
- void setSpeed(int); // Set Movement speed, the higher the faster
- // Zero means no speed control (default)
- void setSpeed(int,bool); // Set compatibility-Flag (true= compatibility with version V08 and earlier)
- #define HIGHRES 0
- #define SPEEDV08 1
-
- uint8_t moving(); // returns the remaining Way to the angle last set with write() in
- // in percentage. '0' means, that the angle is reached
- uint8_t read(); // current position in degrees (0...180)
- int readMicroseconds();// current pulsewidth in microseconds
- uint8_t attached();
- void setMinimumPulse(uint16_t); // pulse length for 0 degrees in microseconds, 700uS default
- void setMaximumPulse(uint16_t); // pulse length for 180 degrees in microseconds, 2300uS default
- };
- //////////////////////////////////////////////////////////////////////////////////////////////
- class SoftLed
- { // Switch leds on/off softly.
- //
- public:
- SoftLed();
- uint8_t attach(uint8_t pinArg, uint8_t invArg = false ); // Led-pin with soft on
- void riseTime( int ); // in millisec - falltime is the same
- void on(); //
- void off(); //
- void write( uint8_t ); // is ON or OFF
- void write( uint8_t time, uint8_t type ); //whether it is a linear or bulb type
- void toggle( void );
- private:
- void mount( uint8_t state );
- ledData_t ledData;
- uint8_t setpoint;
- #define OFF 0
- #define ON 1
- uint8_t ledType; // Type of lamp (linear or bulb)
- #define LINEAR 0
- #define BULB 1
- uint8_t ledIx;
- uint8_t ledValid; // Flag that this is a valid instance
- #define LEDVALID 0x55
- uint8_t ledSpeed; // speed with IRQ based softleds
-
- };
- ////////////////////////////////////////////////////////////////////////////////////////////
- // Timermanagement
- class EggTimer
- {
- public:
- EggTimer();
- void setTime( long);
- bool running();
- long getTime();
- private:
- bool active;
- long endtime;
- };
- #endif
|