gordu 0 Posted April 5, 2013 Share Posted April 5, 2013 I thought it would be pretty straight forward to use the accelStepper lib. First issue was the current release of energia had problems with the inclusion of the math libraries...no problem I grabbed the latest source and compiled it (lots of didling to get all the right libraries but eventually i got there). Started energia, works fiine. Tried some examples, no problem. Then I tried using the accelStepper lib, it compiled fine but when I load it....nothing. the board is dead. I mixed the code with the led blink example and played around a bit and it seems a soon the accelStepper constructor makes any function call I am hooped. If I comment out the function calls in the constructor I am fine the led blinks, but uncomment them or even add a ecall to Serial.print, then nothing. I am at a loss...I dont know how to debug a board that is not (seemingly) running. help Quote Link to post Share on other sites
sq7bti 20 Posted October 5, 2013 Share Posted October 5, 2013 I had similar problem. Perhaps it only worked for me, but you might try the same, so we could verify and that would give some hints to the developers: Rename constructor to a method in AccelStepper.h and AccelStepper.cpp, for example: void AccelStepper::begin(uint8_t interface=AccelStepper::FULL4WIRE, uint8_t pin1=2, uint8_t pin2=3, uint8_t pin3=4, uint8_t pin4=5, bool enable=true) { // paste the original constructor method here } Make an empty default constructor for AccelStepper in AccelStepper.h: AccelStepper::AccelStapper() {} Use it in your code: AccelStepper mystepper; void setup() { mystepper.begin(AccelStepper::FULL4WIRE, coil1, coil2, coil3, coil4); } void loop() { mystepper.runSpeed(); } edit: steps_per_revolution replaced with AccelStepper::FULL4WIRE. with kind regards, Szymon Quote Link to post Share on other sites
sq7bti 20 Posted April 15, 2014 Share Posted April 15, 2014 I decided not to start another thread, because what I am about to present here is actually my attempt to port AccelStepper library to LM4F. Specifically my port makes use of the timer interrupts to schedule stepping, instead of using polled fashion execution of run() method. I used the AccelStepper source code revision 1.39 and I had to move around number of members from private to public: _stepInterval _direction _currentPos _targetPos _speed _pin[4] Modified AccelStepper can be used for inheritance. It is limited to the DRIVER variant, but can be easily extended with FULL4WIRE, HALF4WIRE and others. iAccelStepper.h: #ifndef iAccelStepper_h #define iAccelStepper_h #include <Energia.h> #include "AccelStepper.h" #include "driverlib/sysctl.h" //***************************************************************************** // // The list of Timer peripherals. // //***************************************************************************** static const unsigned long g_ulTIMERPeriph[3] = { #if defined(PART_TM4C1233H6PM) || defined(PART_LM4F120H5QR) // SYSCTL_PERIPH_TIMER0, // wiring_analog.c analogWrite() SYSCTL_PERIPH_TIMER1, SYSCTL_PERIPH_TIMER2, SYSCTL_PERIPH_TIMER3 // SYSCTL_PERIPH_TIMER4, // Tone.c // SYSCTL_PERIPH_TIMER5, // wiring.c - millis() micros() // SYSCTL_PERIPH_TIMER6, // SYSCTL_PERIPH_TIMER7 #else #error "**** No PART defined or unsupported PART ****" #endif }; static const unsigned long g_ulTIMERBase[3] = { #if defined(PART_TM4C1233H6PM) || defined(PART_LM4F120H5QR) // TIMER0_BASE, // wiring_analog.c analogWrite() TIMER1_BASE, TIMER2_BASE, TIMER3_BASE // TIMER4_BASE, // Tone.c // TIMER5_BASE, // wiring.c - millis() micros() // TIMER6_BASE, // TIMER7_BASE #else #error "**** No PART defined or unsupported PART ****" #endif }; class iAccelStepper : public AccelStepper { public: iAccelStepper() : AccelStepper() {}; void begin(uint8_t pin1 = 2, uint8_t pin2 = 3); void moveTo(long absolute); void move(long relative); boolean run(void) { return running; }; void ISR(void); private: boolean running; unsigned int id; }; #endif iAccelStepper.cpp: #include "iAccelStepper.h" #include "driverlib/timer.h" volatile boolean state[3]; static iAccelStepper* me[3]; static unsigned int all_instances; void iAccelStepper::ISR(void) { TimerIntClear(g_ulTIMERBase[id], TIMER_TIMA_TIMEOUT); if(state[id]) { if(_direction == DIRECTION_CW) // Clockwise ++_currentPos; else // Anticlockwise --_currentPos; // prepare for the next period computeNewSpeed(); digitalWrite(_pin[1], _direction); TimerLoadSet(g_ulTIMERBase[id], TIMER_A, 1); TimerEnable(g_ulTIMERBase[id], TIMER_A); } else { // switch off timer at the falling edge if(_stepInterval == 0) { TimerDisable(g_ulTIMERBase[id], TIMER_A); running = false; } else { TimerLoadSet(g_ulTIMERBase[id], TIMER_A, (80*_stepInterval)-1); TimerEnable(g_ulTIMERBase[id], TIMER_A); } } digitalWrite(_pin[0], state[id]); state[id] ^= 1; } void timerISR0(void) { me[0]->ISR(); } void timerISR1(void) { me[1]->ISR(); } void timerISR2(void) { me[2]->ISR(); } //void timerISR3(void) { me[3]->ISR(); } //void timerISR4(void) { me[4]->ISR(); } typedef void (*ISR_ptr_t)(void); //ISR_ptr_t timerISR_ptr[5] = { timerISR0, timerISR1, timerISR2, timerISR3, timerISR4 }; ISR_ptr_t timerISR_ptr[3] = { timerISR0, timerISR1, timerISR2 }; void iAccelStepper::begin(uint8_t pin1, uint8_t pin2) { // STEP DIR AccelStepper::begin(AccelStepper::DRIVER, pin1, pin2); if(all_instances < 3) { id = all_instances; // Configure timer SysCtlPeripheralEnable(g_ulTIMERPeriph[id]); TimerConfigure(g_ulTIMERBase[id], TIMER_CFG_ONE_SHOT); TimerIntEnable(g_ulTIMERBase[id], TIMER_TIMA_TIMEOUT); TimerIntRegister(g_ulTIMERBase[id], TIMER_A, timerISR_ptr[id]); me[id] = this; state[id] = false; running = false; ++all_instances; } } void iAccelStepper::move(long relative) { moveTo(_currentPos + relative); } void iAccelStepper::moveTo(long absolute) { AccelStepper::moveTo(absolute); if(!running && (distanceToGo() != 0)) { digitalWrite(_pin[1], _direction); computeNewSpeed(); state[id] = true; digitalWrite(_pin[0], state[id]); running = true; TimerLoadSet(g_ulTIMERBase[id], TIMER_A, 1); TimerEnable(g_ulTIMERBase[id], TIMER_A); } } Comments are welcome, especially on the usage of other timers: there are only 3 left as far as I can see. Szymon Quote Link to post Share on other sites
sq7bti 20 Posted September 17, 2014 Share Posted September 17, 2014 Hi again, I improved the code further: the driverlib calls were replaced with direct HWREG() writes and reads. I found the timer1 unreliable - as far as I can see it is not used for anything else, and when used for triggering steps it is "stuck" after a while. So far I was not able to find the root-cause of this problem - it is only apparent with timer1 - not with the two remaining timers 2 and 3. https://github.com/sq7bti/iAccelStepper iAccelStepper.h #ifndef iAccelStepper_h #define iAccelStepper_h #include <Energia.h> #include "AccelStepper.h" #include "driverlib/sysctl.h" #define MAX_INST 2 //***************************************************************************** // // The list of Timer peripherals. // //***************************************************************************** static const unsigned long g_ulTIMERPeriph[MAX_INST] = { #if defined(PART_TM4C1233H6PM) || defined(PART_LM4F120H5QR) // SYSCTL_PERIPH_TIMER0, // wiring_analog.c analogWrite() // SYSCTL_PERIPH_TIMER1, SYSCTL_PERIPH_TIMER2, SYSCTL_PERIPH_TIMER3 // SYSCTL_PERIPH_TIMER4, // Tone.c // SYSCTL_PERIPH_TIMER5, // wiring.c - millis() micros() // SYSCTL_PERIPH_TIMER6, // SYSCTL_PERIPH_TIMER7 #else #error "**** No PART defined or unsupported PART ****" #endif }; static const unsigned long g_ulTIMERBase[MAX_INST] = { #if defined(PART_TM4C1233H6PM) || defined(PART_LM4F120H5QR) // TIMER0_BASE, // wiring_analog.c analogWrite() // TIMER1_BASE, TIMER2_BASE, TIMER3_BASE // TIMER4_BASE, // Tone.c // TIMER5_BASE, // wiring.c - millis() micros() // TIMER6_BASE, // TIMER7_BASE #else #error "**** No PART defined or unsupported PART ****" #endif }; class iAccelStepper : public AccelStepper { public: iAccelStepper() : AccelStepper() {}; void begin(uint8_t pin1 = 2, uint8_t pin2 = 3, uint8_t pin3 = 4); void moveTo(long absolute); void move(long relative); boolean run(void) { return running; }; unsigned long stepInterval() { return _stepInterval; }; void ISR(void); private: volatile boolean running; unsigned int id; }; #endif iAccelStepper.cpp #include "iAccelStepper.h" #include "driverlib/timer.h" #include "inc/hw_gpio.h" #include "inc/hw_timer.h" static iAccelStepper* me[MAX_INST]; static uint32_t _port_step[MAX_INST]; static uint8_t _pin_step[MAX_INST]; static uint32_t _port_dir[MAX_INST]; static uint8_t _pin_dir[MAX_INST]; static boolean direction[MAX_INST]; static boolean _state[MAX_INST]; static unsigned char all_instances; static uint32_t ulPeriod; void iAccelStepper::ISR(void) { //TimerIntClear(g_ulTIMERBase[id], TIMER_TIMA_TIMEOUT); HWREG(g_ulTIMERBase[id] + TIMER_O_ICR) = TIMER_TIMA_TIMEOUT; // falling edge produce space for length _stepInterval if(_state[id]) { _state[id] = false; // prepare for the next period // falling edge - calculate everything necessary and calculate _stepInterval computeNewSpeed(); if(direction[id] != _direction) { direction[id] = _direction; HWREG(_port_dir[id]) = _direction?_pin_dir[id]:0; } HWREG(_port_step[id]) = 0; //TimerLoadSet(g_ulTIMERBase[id], TIMER_A, _stepInterval - ulPeriod); HWREG(g_ulTIMERBase[id] + TIMER_O_TAILR) = _stepInterval - ulPeriod; //TimerEnable(g_ulTIMERBase[id], TIMER_A); HWREG(g_ulTIMERBase[id] + TIMER_O_CTL) |= TIMER_A & (TIMER_CTL_TAEN | TIMER_CTL_TBEN); } else { // either fire the timer again for another period or switch it off when the move is finished if((_stepInterval == 0) || (abs(distanceToGo()) < 1)) { //TimerDisable(g_ulTIMERBase[id], TIMER_A); HWREG(g_ulTIMERBase[id] + TIMER_O_CTL) &= ~(TIMER_A & (TIMER_CTL_TAEN | TIMER_CTL_TBEN)); running = false; } else { _state[id] = true; if(_direction == DIRECTION_CW) // Clockwise ++_currentPos; else // Anticlockwise --_currentPos; //TimerLoadSet(g_ulTIMERBase[id], TIMER_A, ulPeriod); HWREG(g_ulTIMERBase[id] + TIMER_O_TAILR) = ulPeriod; HWREG(_port_step[id]) = _pin_step[id]; //TimerEnable(g_ulTIMERBase[id], TIMER_A); HWREG(g_ulTIMERBase[id] + TIMER_O_CTL) |= TIMER_A & (TIMER_CTL_TAEN | TIMER_CTL_TBEN); } } } void timerISR0(void) { me[0]->ISR(); } void timerISR1(void) { me[1]->ISR(); } //void timerISR2(void) { me[2]->ISR(); } //void timerISR3(void) { me[3]->ISR(); } //void timerISR4(void) { me[4]->ISR(); } typedef void (*ISR_ptr_t)(void); //ISR_ptr_t timerISR_ptr[5] = { &timerISR0, &timerISR1, timerISR2, timerISR3, timerISR4 }; ISR_ptr_t timerISR_ptr[MAX_INST] = { timerISR0, timerISR1 }; void iAccelStepper::begin(uint8_t pin1, uint8_t pin2, uint8_t pin3) { // STEP DIR AccelStepper::begin(AccelStepper::DRIVER, pin1, pin2); // ENABLE AccelStepper::setEnablePin(pin3); AccelStepper::setPinsInverted(false, false, true); // specs of DRV8825 requires 2us, A3967 and A4988 requires atleast 1us step pulse // ulPeriod = clockCyclesPerMicrosecond() * 2; ulPeriod = 1; if(all_instances < MAX_INST) { id = all_instances; // Configure timer SysCtlPeripheralEnable(g_ulTIMERPeriph[id]); TimerConfigure(g_ulTIMERBase[id], TIMER_CFG_ONE_SHOT); //TimerIntEnable(g_ulTIMERBase[id], TIMER_TIMA_TIMEOUT); HWREG(g_ulTIMERBase[id] + TIMER_O_IMR) |= TIMER_TIMA_TIMEOUT; TimerIntRegister(g_ulTIMERBase[id], TIMER_A, timerISR_ptr[id]); me[id] = this; running = false; ++all_instances; _port_step[id] = (uint32_t)portBASERegister(digitalPinToPort(pin1)) + (GPIO_O_DATA + (digitalPinToBitMask(pin1) << 2)); _pin_step[id] = (uint8_t)digitalPinToBitMask(pin1); _port_dir[id] = (uint32_t)portBASERegister(digitalPinToPort(pin2)) + (GPIO_O_DATA + (digitalPinToBitMask(pin2) << 2)); _pin_dir[id] = (uint8_t)digitalPinToBitMask(pin2); direction[id] = false; _state[id] = false; } } void iAccelStepper::move(long relative) { moveTo(_currentPos + relative); } void iAccelStepper::moveTo(long absolute) { AccelStepper::moveTo(absolute); if(!running && (distanceToGo() != 0)) { running = true; computeNewSpeed(); if(direction[id] != _direction) { direction[id] = _direction; HWREG(_port_dir[id]) = _direction?_pin_dir[id]:0; } HWREG(_port_step[id]) = 0; _state[id] = false; //TimerLoadSet(g_ulTIMERBase[id], TIMER_A, _stepInterval - ulPeriod); HWREG(g_ulTIMERBase[id] + TIMER_O_TAILR) = _stepInterval - ulPeriod; //TimerEnable(g_ulTIMERBase[id], TIMER_A); HWREG(g_ulTIMERBase[id] + TIMER_O_CTL) |= TIMER_A & (TIMER_CTL_TAEN | TIMER_CTL_TBEN); } } Inheritance prerequisites remains as described in previous post. Remarks welcome. With kind regards, Szymon /EDIT: added github Quote Link to post Share on other sites
igor 163 Posted September 17, 2014 Share Posted September 17, 2014 Few suggestions - I decided not to start another thread, because what I am about to present here is actually my attempt to port AccelStepper library to LM4F. Specifically my port makes use of the timer interrupts to schedule stepping, instead of using polled fashion execution of run() method. I used the AccelStepper source code revision 1.39 and I had to move around number of members from private to public: _stepInterval _direction _currentPos _targetPos _speed _pin[4] Consider making these variables protected, rather than public. Then can use them in subclass without interference from outside. Move g_ulTIMERPeriph and g_ulTIMERBase into iAccelStepper.cpp (that looks like only place they are used). Make the timer ISRs (timerISR0, timerISR1, etc.) friends, so that can make iAccelStepper::ISR protected. (Helps with information hiding, might also help compiler in deciding about inlining ISR. Might be worth noting in the comments about timer selection that the Servo library uses timer 2 and that timers 0 through 3 may be used by analog write (depending on which pin(s) you are writing to.) Might think about adding support for wide timers - just to give more flexibility in selecting a timer that isn't used by something else. Quote Link to post Share on other sites
sq7bti 20 Posted September 17, 2014 Share Posted September 17, 2014 Thanks for your input. BTW, Regarding the timer used by other libraries I also discovered why I had problems with Timer1: there was a conflict because in my project I am using pin PF_3 for PWM generation with analogWrite(). Perhaps it would be even more flexible yet safe to allow selecting (with begin() method) a specific timer instead of choosing timers one by one from the list of hard-coded list of available ones. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.