繁体   English   中英

Arduino MEGA - 为什么我的定时器中断在第一次运行时不正确?

[英]Arduino MEGA - Why is my timer interrupt incorrect on first run?

我正在使用 Arduino 计时器为 Arduino MEGA 2560 编写步进驱动程序类,但我遇到了一些意外行为,因为使用的计时器。 我要追求的本质如下:

  • Stepper Speed Ramp Class:包含速度斜坡信息,如最大速度、加速度、减速度、OCR 值数组等。
  • CL42T 类:该类(公开)继承了 Stepper Speed Ramp 类,并包含定时器寄存器指针、I/O 端口寄存器指针、定时器掩码和一些其他成员变量。

为了测试这个类结构,我写了一个草图来测试运行步进电机一圈(子组件),提高速度,然后重复。 不幸的是,在我的草图的第一个循环(以及第四个循环)期间,我遇到了非常奇怪的计时器行为,其中计时器的频率似乎比预期的要高得多,因此以某种可怕的速度运行电机并且不是当前设置的“maxSpeed”。 循环 2 和 3 以及最多 10 次都显示了预期的行为,但我没有超过 10 次来寻找任何更深层次的模式。

我知道 init() 函数在设置之前会调整定时器寄存器,所以我在 setup() 中清除了定时器 1 到 5 的控制寄存器。 我想在这个应用程序中潜在地使用 micros() 和/或 millis(),所以我避免弄乱定时器 0。

在我进行了所有故障排除之后,我一直与两个潜在的根本原因保持一致(尽管我似乎无法找到两者的原因):

  • 通过类结构,我以不正确的顺序或类似的顺序设置定时器控制寄存器。
  • 某种溢出或其他计算/数据类型错误。 我已经浏览了这个,并查找了所有的 C++ 标准促销,但我似乎无法找到任何会导致 CalculateSpeedRamp() 成员函数出现问题的地方。 此外,我已经打印出 OCR 数组值并根据手动计算确认了它们 - 没有发现问题。

我使用的是 VS Code v1.62.0、Arduino 扩展 v0.4.7 和 C/C++ 扩展 v1.7.1。

下面是我的代码,以及运行我的测试的串行监视器输出。 任何人都可以找到我出错的地方吗? 预先感谢您的帮助!

CL42TClassTest.ino:

#include "CL42T.h"

// prescale value
int PreScale = 8;

// Step Counter
volatile unsigned long TestStepperSteps = 0;
bool oneShot = true;

const char TestStepperStepPort = 'E';
const int TestStepperStepBit = 3;
const int TestStepperControlBit = 2;
const int TestStepperTimerNum = 3;
const char TestStepperTimerOutput = 'A';
const int TestStepperOneRevSteps = 27774;

// create test stepper object (global object)
CL42T TestStepperDriveObject(TestStepperStepPort,TestStepperStepBit,TestStepperControlBit,TestStepperTimerNum,TestStepperTimerOutput,PreScale);

// Macros for anscillary equipment
#define VDC24_Setup DDRF |= B00010000   // set 24VDC Control Relay pin to OUTPUT
#define VDC24_On    PORTF |= B00010000  // set 24VDC Control Relay pin HIGH
#define VDC24_Off   PORTF &= B11101111  // set 24VDC Control Relay pin LOW

// max speed variable
int MAXspeed = 60; 

void setup() {
    Serial.begin(115200); // start serial at 115200 BAUD
    while (!Serial) {
        ; // wait for serial to connect
    }
    // header
    Serial.println(F("Program to test CL42T Class."));
    // clear timer registers and print verification
    clearTimersOneToFive();
    Serial.println(F("Timers 1-5 Register Values After Reset:"));
    // proceed to print out all timer 1-5 control registers, TCNT values, and OCR values...

    // setup 24VDC relay and ensure OFF
    Serial.print(F("Setting up 24VDC control relay..."));
    VDC24_Setup;
    VDC24_Off;
    Serial.println(F("Done."));
    // setup stepper drive object
    Serial.print(F("Setting up Test Stepper drive object..."));
    TestStepperDriveObject.SetupDrive();
    Serial.println(F("Done."));
    // Timer settings printout
    Serial.println(F("Setup Timer Settings: "));
    Serial.print(F("OCR3A Value: "));Serial.println(OCR3A);
    Serial.print(F("TCNT3 Value: "));Serial.println(TCNT3);;
    Serial.print(F("TCCR3A Value: "));prntByteBIN(TCCR3A);
    Serial.print(F("TCCR3B Value: "));prntByteBIN(TCCR3B);
    Serial.print(F("TCCR3C Value: "));prntByteBIN(TCCR3C);
    Serial.print(F("TIMSK3 Value: "));prntByteBIN(TIMSK3);
    Serial.print(F("TIFR3 Value: "));prntByteBIN(TIFR3);
    // Step pin port values printout
    Serial.println(F("Setup DDRE and PORTE: "));
    Serial.print(F("DDRE = "));prntByteBIN(DDRE);
    Serial.print(F("PORTE = "));prntByteBIN(PINE);
    // indication for main loop start
    Serial.println(F("Now starting main loop..."));
    delay(1000);
}

void loop()
{
    if (oneShot)
    {
        // Timer settings printout
        Serial.println(F("First Loop Timer Settings:"));
        // procede to print out all timer 1-5 control registers, TCNT, and OCR

        // Step pin port values printout
        Serial.println(F("First Loop DDRE and PORTE:"));
        Serial.print(F("DDRE = "));prntByteBIN(DDRE);
        Serial.print(F("PORTE = "));prntByteBIN(PINE);
        // CL42T class settings
        Serial.println(F("C42T Class Settings: "));
        // proceed to print out all CL42T object values (addresses, masks, etc.)

        // StepperSpeedRamp class settings
        Serial.println(F("StepperSpeedRamp Class Settings:"));
        // proceed to print out all StepperSpeedRamp object values
    }
    // set maxSpeed of drive & calculate OCR array, then apply
    TestStepperDriveObject.SetMaxSpeed(MAXspeed);
    TestStepperDriveObject.ApplyNewSpeed();
    // turn on 24 VDC
    Serial.print(F("Turning on 24VDC Control Relay..."));
    VDC24_On;
    Serial.println(F("Done."));
    delay(1000);
    // enable drive
    Serial.print(F("Enabling Test Stepper Drive..."));
    TestStepperDriveObject.EnableDrive();
    Serial.println(F("Done."));
    delayMicroseconds(500);
    // set drive direction
    Serial.print(F("Setting Test Stepper Direction to FWD..."));
    TestStepperDriveObject.SetDirection('F');
    Serial.println(F("Done."));
    delayMicroseconds(500);
    // move stepper drive one revolution
    Serial.print(F("Starting Test Stepper at "));Serial.print(MAXspeed);Serial.println(F(" mm/s for one revolution..."));
    TestStepperDriveObject.Increment(TestStepperOneRevSteps);
    // monitor for end of movement
    while (TestStepperSteps < TestStepperDriveObject.incrementSteps)
    {
        Serial.print(F("Steps taken: "));Serial.println(TestStepperSteps);
        delay(250);
    }
    Serial.println(F("Done."));
    // clear step tracker
    TestStepperSteps = 0;
    delayMicroseconds(500);
    // disable stepper drive
    Serial.print(F("Disabling Test Stepper Drive..."));
    TestStepperDriveObject.DisableDrive();
    Serial.println(F("Done."));
    delayMicroseconds(500);
    // turn off 24VDC
    Serial.print(F("Turning off 24VDC Control Relay..."));
    VDC24_Off;
    Serial.println(F("Done."));
    // adjust max speed up 20 mm/s
    MAXspeed += 20;
    // delay to slow test progression
    delay(3000);
}

void prntByteBIN(byte b)
{
    for(int i = 7; i >= 0; i--)
    {
        Serial.print(bitRead(b,i));
    }
    Serial.println();
}

void clearTimersOneToFive()
{
    noInterrupts();
    TCCR1A = 0;
    TCCR1B = 0;
    TCCR1C = 0;
    TIMSK1 = 0;
    TCNT1H = 0;
    TCNT1L = 0;
    TIFR1 |= B00001111;
    TCCR2A = 0;
    TCCR2B = 0;
    TIMSK2 = 0;
    TCNT2 = 0;
    TIFR2 |= B00001111;
    TCCR3A = 0;
    TCCR3B = 0;
    TCCR3C = 0;
    TIMSK3 = 0;
    TCNT3H = 0;
    TCNT3L = 0;
    TIFR3 |= B00001111;
    TCCR4A = 0;
    TCCR4B = 0;
    TCCR4C = 0;
    TIMSK4 = 0;
    TCNT4H = 0;
    TCNT4L = 0;
    TIFR4 |= B00001111;
    TCCR5A = 0;
    TCCR5B = 0;
    TCCR5C = 0;
    TIMSK5 = 0;
    TCNT5H = 0;
    TCNT5L = 0;
    TIFR5 |= B00001111;
    interrupts();
}

// Test Stepper Step Interrupt
ISR(TIMER3_COMPA_vect) {
    if (((*TestStepperDriveObject.stepPIN) & (1<<TestStepperDriveObject.stepBIT))) {
        TestStepperSteps++;        
        if ((TestStepperDriveObject.incrementSteps > 0) && (TestStepperSteps >= TestStepperDriveObject.incrementSteps))
        {
            TestStepperDriveObject.StopDrive(); // position reached, stop drive
            // TestStepperSteps = 0;
        }
        if (TestStepperSteps <= TestStepperDriveObject.accelSteps)
        { // acceleration phase
            if (!(TestStepperSteps % TestStepperDriveObject.speedUpdateSteps))
            {
                TestStepperDriveObject.accelDecelIndex++;
                *TestStepperDriveObject.stepOCRH = highByte(TestStepperDriveObject.OCRarray[TestStepperDriveObject.accelDecelIndex]);
                *TestStepperDriveObject.stepOCRL = lowByte(TestStepperDriveObject.OCRarray[TestStepperDriveObject.accelDecelIndex]);
            }
        }
        if ((TestStepperDriveObject.incrementSteps > 0) && (TestStepperSteps >= (TestStepperDriveObject.incrementSteps - TestStepperDriveObject.decelSteps)))
        { // deceleration phase - position tracking only
            if (!(TestStepperSteps % TestStepperDriveObject.speedUpdateSteps))
            {
                *TestStepperDriveObject.stepOCRH = highByte(TestStepperDriveObject.OCRarray[TestStepperDriveObject.accelDecelIndex]);
                *TestStepperDriveObject.stepOCRL = lowByte(TestStepperDriveObject.OCRarray[TestStepperDriveObject.accelDecelIndex]);
                TestStepperDriveObject.accelDecelIndex--;
            }
        }
    }
}

CL42T.h:

#ifndef CL42T_h
#define CL42T_h

#include "Arduino.h"
#include "StepperSpeedRamp.h"

class CL42T: public StepperSpeedRamp
{
public:
    // register pointers
    volatile uint8_t *directionPORT;
    volatile uint8_t *directionDDR;
    volatile uint8_t *directionPIN;
    volatile uint8_t *enablePORT;
    volatile uint8_t *enableDDR;
    volatile uint8_t *enablePIN;
    volatile uint8_t *alarmPORT;
    volatile uint8_t *alarmDDR;
    volatile uint8_t *alarmPIN;
    volatile uint8_t *stepPORT;
    volatile uint8_t *stepDDR;
    volatile uint8_t *stepPIN;
    volatile uint8_t *stepTCCRA;
    volatile uint8_t *stepTCCRB;
    volatile uint8_t *stepTCCRC;
    volatile uint8_t *stepTIMSK;
    volatile uint8_t *stepTIFR;
    volatile uint8_t *stepTCNTH;
    volatile uint8_t *stepTCNTL;
    volatile uint8_t *stepOCRH;
    volatile uint8_t *stepOCRL;
    volatile uint8_t *stepOCRH2;
    volatile uint8_t *stepOCRL2;
    // timer register masks
    uint8_t stepTCCRAmask;
    uint8_t stepTCCRAOutput1Mask;
    uint8_t stepTCCRAOutput2Mask;
    uint8_t stepTCCRBmask;
    uint8_t stepTCCRBstartMask;
    uint8_t stepTimerOutputInterrupt1Mask;
    // dual or single motor toggle variable
    volatile int numMOTORS;
    // step and control bits
    volatile int stepBIT;
    volatile int stepBIT2;
    volatile int controlBIT;
    volatile int controlBIT2;
    // step target value
    volatile int incrementSteps;
    // single motor constructor:
    CL42T(char StepPort, int StepBit, int ControlBit, int TimerNum, char TimerOutput, int PreScale);
    // dual motor constructor:
    CL42T(char StepPort1, int StepBit1, int ControlBit1, int TimerNum1, char TimerOutput1, int StepBit2, int ControlBit2, char TimerOutput2, int PreScale);
    void SetupDrive(); // setup pointers, port directions, etc.
    void ApplyNewSpeed(); // set OCRL/OCRH to OCRarray[0]
    void SetDirection(char DriveDir); // set direction of CL42T stepper driver
    void EnableDrive(); // enable CL42T stepper driver
    void DisableDrive(); // disable CL42T stepper driver
    void RunDrive(); // run single mmotor or both motors
    void StopDrive(); // stop single motor or both motors
    void Increment(int IncrementSteps); // run at maxSpeed for IncrementSteps
};
#endif

CL42T.cpp:

#include "Arduino.h"
#include "CL42T.h"

/*
* Single Motor Constructor:
* - set numMOTORS to 1
* - copy relevant parameters
* - set step pointers
* - set timer pointers and timer masks
*/
CL42T::CL42T(char StepPort, int StepBit, int ControlBit, int TimerNum, char TimerOutput, int PreScale)
        :StepperSpeedRamp(PreScale)
{
    // this is the single motor constructor - sets timer register pointers and timer register masks.
    
}

/*
* Dual Motor Constructor:
* - set numMOTORS to 2
* - copy relevant parameters
* - set step pointers
* - set timer pointers and timer masks
*/
CL42T::CL42T(char StepPort, int StepBit1, int ControlBit1, int TimerNum, char TimerOutput1, int StepBit2, int ControlBit2, char TimerOutput2, int PreScale)
        :StepperSpeedRamp(PreScale)
{
    // this is the dual motor constructor - sets timer register pointers and timer register masks.
   
}

/*
* Setup:
* - Set control I/O registers
* - Clear timer registers
* - Setup timer, but don't start it
*/ 
void CL42T::SetupDrive()
{
    // clear timer registers and any pending interrupt flags, set timer control registers, clear TCNT, and set initial OCR value. This function DOES NOT set the clock select bits, as this is not the desired point for the timer to run.
  
}

void CL42T::ApplyNewSpeed()
{ // set OCR value from OCRarray[0]
    
}

void CL42T::SetDirection(char DriveDir)
{
    // set direction pin LOW or HIGH
}

void CL42T::EnableDrive()
{
    // set enable pin HIGH
}

void CL42T::DisableDrive()
{
    // set enable pin LOW
}

void CL42T::RunDrive()
{   
    // TCNT to zero, clear pending timer compare match interrupt flags, set clock select bits to start timer
}

void CL42T::StopDrive()
{
    // clear clock select bits in TCCRB to stop timer, disable timer interrupt, clear TCNT
}

void CL42T::Increment(int IncrementSteps)
{
    // set target steps, then call RunDrive()
}

StepperSpeedRamp.h:

#ifndef StepperSpeedRamp_h
#define StepperSpeedRamp_h

#include "Arduino.h"

// Stepper Speed Ramp Class for CL42T drives
class StepperSpeedRamp
{
public:
    // drive settings
    double maxSpeed; // mm/s
    // variables for speed ramp calculations
    double Accel; // mm/s/s
    double Decel; // mm/s/s
    double mmPerStep; // mm/step
    int preScale; // prescale selection
    long CntFreq; // counter frequency
    int speedUpdateSteps; // # steps
    long maxSpeedSteps; // # steps
    long maxAccelSteps; // # steps
    long accelSteps; // # steps
    long decelSteps; // # steps
    long numStepsTemp; // # steps
    long OCRmin;
    long OCRo;
    // speed update index (OCRarray[])
    volatile int accelDecelIndex;
    // speed ramp OCR values
    volatile uint16_t OCRarray[21];
    // member functions
    StepperSpeedRamp(int PreScale);
    void SetMaxSpeed(int MmPerSec);
    void CalculateSpeedRamp();
};
#endif

StepperSpeedRamp.cpp:

#include "Arduino.h"
#include "math.h"
#include "StepperSpeedRamp.h"

/*
* Stepper Speed Ramp Class
*/
StepperSpeedRamp::StepperSpeedRamp(int PreScale)
{
    // drive settings
    maxSpeed = 60; // mm/s - default @ 60
    // variables for speed ramp calculations
    Accel = maxSpeed*10; // mm/s/s - accelerate in 100 ms
    Decel = maxSpeed*10; // mm/s/s - decelerate in 100 ms
    mmPerStep = 0.04; // mm/step
    preScale = PreScale; // copy prescale value into object
    CntFreq = 16000000/preScale; // calculate counter frequency at PreScale
    speedUpdateSteps = 0; // # steps
    maxSpeedSteps = 0; // # steps
    maxAccelSteps = 0; // # steps
    accelSteps = 0; // # steps
    decelSteps = 0; // # steps
    numStepsTemp = 10000; // use 10,000 steps as the calculation for speed ramp
    OCRmin = 0; // minimum delay --> max speed
    OCRo = 0; // first delay --> start speed (based on acceleration)
    accelDecelIndex = 0; // speed update index (OCRarray[])
    CalculateSpeedRamp(); // calculate OCRarray
}

void StepperSpeedRamp::SetMaxSpeed(int MmPerSec)
{
    maxSpeed = MmPerSec; // set new maxSpeed value
    Accel = maxSpeed*10; // accelerate in 100 ms
    Decel = maxSpeed*10; // decelerate in 100 ms
    CalculateSpeedRamp(); // calculate new OCRarray
}

void StepperSpeedRamp::CalculateSpeedRamp()
{
    OCRmin = ((mmPerStep*(CntFreq/2))/(maxSpeed)); // calculate minimum OCR - based on max speed
    OCRo = ((CntFreq/2)*sqrt((2*mmPerStep)/Accel)); // calculate first OCR value - based on accel
    OCRarray[0] = OCRo; // load first OCR value into OCRarray
    maxSpeedSteps = ((maxSpeed*maxSpeed)/(2*mmPerStep*Accel)); // calculate steps required to reach max speed
    maxAccelSteps = ((numStepsTemp*Decel)/(Accel+Decel)); // calculate steps required to reach max deceleration phase
    if (maxSpeedSteps < maxAccelSteps)
    { // trapezoidal speed profile
        accelSteps = maxSpeedSteps; 
        decelSteps = (accelSteps*(Accel/Decel));
    }
    else if (maxSpeedSteps >= maxAccelSteps)
    { // triangular speed profile
        accelSteps = maxAccelSteps;
        decelSteps = (accelSteps*(Accel/Decel));
    }
    speedUpdateSteps = accelSteps / ((sizeof(OCRarray)/2)-2); // 0 indexed AND OCR[0] is already filled.
    for (int i = 1;i < (sizeof(OCRarray)/2);i++)
    { // populate OCR array
        OCRarray[i] = (OCRo*(sqrt((i*speedUpdateSteps)+1)-sqrt((i*speedUpdateSteps))));
    }
}

好吧,老实说,我认为您提供了太多不相关的代码,这使得您很难分析您放在一起的内容。 Atmel 助记符(例如 TCCR1A)并没有让它变得更容易。 我可以推荐的是将一组 LED 连接到输出端口(您打算连接到步进电机的那些),减慢计时器的速度,以便 ISR 每几秒触发一次,并验证您获得正确的信号模式。 从那里开始,增加频率并继续前进。

好的,我已经剥离了代码,并将它们组合成一个文件,以简化对架构概念的测试(代码嵌入在最后)。

我想我已经找到了确凿的证据——我的 OCR 寄存器设置得太低,可能是加速阶段的最后一次分配。 我通过在打印当前步数的同时打印当前 OCR 值来捕获它。 这是显示吸烟枪的串行输出的片段:

Setting max speed...Done.
maxSpeed = 60
Accel = 600
Decel = 600
maxSpeedSteps = 75
maxAccelSteps = 5000
accelSteps = 75
decelSteps = 75
OCRo = 11547
OCRmin = 666
OCR 1 = 3094
OCR 2 = 2266
OCR 3 = 1873
OCR 4 = 1633
OCR 5 = 1466
OCR 6 = 1342
OCR 7 = 1245
OCR 8 = 1166
OCR 9 = 1101
OCR 10 = 1045
OCR 11 = 997
OCR 12 = 955
OCR 13 = 918
OCR 14 = 885
OCR 15 = 855
OCR 16 = 829
OCR 17 = 804
OCR 18 = 782
OCR 19 = 761
OCR 20 = 742
Starting movement...
OCR3A = 11547
Steps taken: 60
OCR3A = 742
Steps taken: 298
OCR3A = 144 <---------- OCR somehow too low
Steps taken: 1002
OCR3A = 144 <---------- OCR somehow too low
Steps taken: 1707
OCR3A = 144 <---------- OCR somehow too low
Steps taken: 2412
OCR3A = 144 <---------- OCR somehow too low
Steps taken: 3117
OCR3A = 144 <---------- OCR somehow too low
Steps taken: 3822
OCR3A = 144 <---------- OCR somehow too low
... // and it continues on until deceleration, which behaves as it should.

我的假设是我正在为 OCR 寄存器分配一个值,该值超出了我的 OCR 数组的范围。 但是,我不确定它是如何发生的。 步数只在加速阶段和减速阶段更新,速度更新“距离”(调整 OCR 之前的步数)是根据加速步数计算的,所以我看不出如何得到数组之间的不匹配索引和数组边界。 有任何想法吗?

代码:

#include "math.h"

class SpeedRamp
{
public:
// private:
    long maxSpeed; // mm/s
    long Accel; // mm/s/s
    long Decel; // mm/s/s
    double mmPerStep;
    int preScale;
    long CntFreq;
    long maxSpeedSteps;
    long maxAccelSteps;
    long numStepsTemp;
    uint16_t OCRmin;
    uint16_t OCRo;
// public:
    long accelSteps;
    long decelSteps;
    int speedUpdateSteps;
    volatile int accelDecelIndex;
    volatile uint16_t OCRarray[21];
    SpeedRamp();
    void SetMaxSpeed(int MmPerSec);
    void CalculateSpeedRamp();
};

SpeedRamp::SpeedRamp()
{
    maxSpeed = 60; // default to 60 mm/s
    Accel = maxSpeed*10; // accelerate in 100 ms
    Decel = maxSpeed*10; // decelerate in 100 ms
    mmPerStep = 0.04;
    preScale = 8; 
    CntFreq = 16000000L/preScale; // calculate timer count frequency based on prescale
    numStepsTemp = 10000; // use 10,000 steps as speed ramp calculation
    CalculateSpeedRamp();
}

void SpeedRamp::SetMaxSpeed(int MmPerSec)
{
    maxSpeed = MmPerSec;
    Accel = maxSpeed*10; // accelerate in 100 ms
    Decel = maxSpeed*10; // decelerate in 100 ms
    CalculateSpeedRamp();
}

void SpeedRamp::CalculateSpeedRamp()
{
    OCRmin = ((mmPerStep*(CntFreq/2))/(maxSpeed));
    OCRo = ((CntFreq/2)*sqrt((2*mmPerStep)/Accel));
    OCRarray[0] = OCRo;
    maxSpeedSteps = ((maxSpeed*maxSpeed)/(2*mmPerStep*Accel));
    maxAccelSteps = ((numStepsTemp*Decel)/(Accel+Decel));
    if (maxSpeedSteps < maxAccelSteps)
    {
        accelSteps = maxSpeedSteps;
        decelSteps = (accelSteps*(Accel/Decel));
    }
    else
    {
        accelSteps = maxAccelSteps;
        decelSteps = (accelSteps*(Accel/Decel));
    }
    speedUpdateSteps = round(accelSteps/(sizeof(OCRarray)/2));
    for (int i = 0; i < (sizeof(OCRarray)/2); i++)
    {
        OCRarray[i] = (OCRo*(sqrt((i*speedUpdateSteps)+1) - sqrt((i*speedUpdateSteps))));
    }
}

class StepperDrive: public SpeedRamp
{
public:
    volatile uint8_t *stepPORT;
    int stepBit;
    volatile uint8_t *stepDDR;
    volatile uint8_t *stepPIN;
    volatile uint8_t *stepTCCRA;
    volatile uint8_t *stepTCCRB;
    volatile uint8_t *stepTCCRC;
    volatile uint8_t *stepTIMSK;
    volatile uint8_t *stepTIFR;
    volatile uint16_t *stepOCRA;
    volatile uint16_t *stepTCNT;
    uint8_t stepTCCRAmask;
    uint8_t stepTCCRAOutputMask;
    uint8_t stepTCCRBmask;
    uint8_t stepTCCRBStartMask;
    uint8_t stepTimerOutputInterruptMask;
    long incrementSteps;
    long stepsTaken;
    StepperDrive();
    void SetupDrive();
    void SetMaxSpeed(double MmPerSec);
    void RunDrive();
    void StopDrive();
    void Increment(long IncrementSteps);
};

StepperDrive::StepperDrive()
            :SpeedRamp()
{
    stepPORT = &PORTE;
    stepBit = 3; // PE3
    stepDDR = &DDRE;
    stepPIN = &PINE;
    stepTCCRA = &TCCR3A;
    stepTCCRB = &TCCR3B;
    stepTCCRC = &TCCR3C;
    stepTIMSK = &TIMSK3;
    stepTIFR = &TIFR3;
    stepOCRA = &OCR3A;
    stepTCNT = &TCNT3;
    stepTCCRAmask = B00000000;
    stepTCCRAOutputMask = B01000000; // COM3A0 = 1 (toggle on compare match)
    stepTCCRBmask = B00001000; // WGM32 = 1 (CTC, mode 4)
    stepTCCRBStartMask = B00000010; // CS31 = 1 (prescale 8)
    stepTimerOutputInterruptMask = B00000010; // OCIE3A = 1 (enable interrupt A)
    stepsTaken = 0;
    incrementSteps = 0;
}

void StepperDrive::SetupDrive()
{
    // clear timer registers
    *stepTCCRA = 0;
    *stepTCCRB = 0;
    *stepTCCRC = 0;
    *stepTIMSK = 0;
    *stepTCNT = 0;
}

void StepperDrive::SetMaxSpeed(double MmPerSec)
{
    SpeedRamp::SetMaxSpeed(MmPerSec); // adjust private maxSpeed variable
}

void StepperDrive::RunDrive()
{
    if (incrementSteps == 0)
    {
        incrementSteps = -1; // if no position target, give indication to ISR
    }
    accelDecelIndex = 0; // reset accelDecelIndex (OCRarray[])
    stepsTaken = 0; // reset stepsTaken
    *stepTCNT = 0; // reset timer count
    noInterrupts();
    *stepOCRA = OCRarray[0]; // set first OCR value
    interrupts();
    *stepDDR |= (1<<stepBit); // set step pin as OUTPUT
    *stepPORT &= ~(1<<stepBit); // set step pin LOW
    *stepTCCRA = (stepTCCRAOutputMask); // enable COM3A toggle on compare match
    *stepTIMSK = (stepTimerOutputInterruptMask); // enable compare match interrupt
    *stepTIFR |= B00001111; // clear any pending interrupts
    *stepTCCRB = (stepTCCRBmask | stepTCCRBStartMask); // place timer in CTC mode, start timer at prescale 8
}

void StepperDrive::StopDrive()
{
    *stepTCCRB = 0; // stop timer
    *stepTCCRA = 0; 
    *stepTIMSK = 0; // clear interrupt enable
    *stepPORT &= ~(1<<stepBit); // pull step pin LOW
}

void StepperDrive::Increment(long IncrementSteps)
{
    incrementSteps = IncrementSteps; // set position target
    RunDrive();
}

// global stepper drive object
StepperDrive stepperTest;

// global MAXspeed variable
double MAXspeed = 60;

void setup() {
    Serial.begin(115200);
    while (!Serial)
    {
        ; // wait for serial to connect
    }
    delay(1000);
    Serial.print(F("Setting up stepper object..."));
    stepperTest.SetupDrive();
    Serial.println(F("Done."));
    Serial.println(F("Now starting main loop."));
}

void loop() {
    Serial.print(F("Setting max speed..."));
    stepperTest.SetMaxSpeed(MAXspeed);
    Serial.println(F("Done."));
    Serial.print(F("maxSpeed = "));Serial.println(stepperTest.maxSpeed);
    Serial.print(F("Accel = "));Serial.println(stepperTest.Accel);
    Serial.print(F("Decel = "));Serial.println(stepperTest.Decel);
    Serial.print(F("maxSpeedSteps = "));Serial.println(stepperTest.maxSpeedSteps);
    Serial.print(F("maxAccelSteps = "));Serial.println(stepperTest.maxAccelSteps);
    Serial.print(F("accelSteps = "));Serial.println(stepperTest.accelSteps);
    Serial.print(F("decelSteps = "));Serial.println(stepperTest.decelSteps);
    Serial.print(F("OCRo = "));Serial.println(stepperTest.OCRo);
    Serial.print(F("OCRmin = "));Serial.println(stepperTest.OCRmin);
    Serial.println(F("OCR array:"));
    for (int i = 0; i < (sizeof(stepperTest.OCRarray)/2); i++)
    {
        Serial.print(F("OCR "));Serial.print(i);Serial.print(F(" = "));Serial.println(stepperTest.OCRarray[i]);
    }
    Serial.println(F("Starting movement..."));
    stepperTest.Increment(27774);
    Serial.print(F("OCR3A = "));Serial.println(OCR3A);
    while (stepperTest.stepsTaken < stepperTest.incrementSteps)
    {
        delay(100);
        Serial.print(F("Steps taken: "));Serial.println(stepperTest.stepsTaken);
        Serial.print(F("OCR3A = "));Serial.println(OCR3A);
    }
    Serial.println(F("Move complete."));
    stepperTest.stepsTaken = 0;
    MAXspeed += 10;
    delay(3000);
}

ISR(TIMER3_COMPA_vect)
{ // OC3A is on pin PE3, controlled by OCR3A
    if (PINE & (1<<3))
    { // step pin is HIGH
        stepperTest.stepsTaken++;
        if ((stepperTest.incrementSteps > 0) && (stepperTest.stepsTaken >= stepperTest.incrementSteps))
        { // if step target exists and has been reached, stop drive - targeted moves only
            stepperTest.StopDrive();
        }
        if (!(stepperTest.stepsTaken % stepperTest.speedUpdateSteps)&&(stepperTest.stepsTaken <= stepperTest.accelSteps))
        { // acceleration phase - all moves
            stepperTest.accelDecelIndex++;
            if (stepperTest.OCRarray[stepperTest.accelDecelIndex] <= stepperTest.OCRmin)
            {
                OCR3A = stepperTest.OCRmin;
            }
            else
            {
                OCR3A = stepperTest.OCRarray[stepperTest.accelDecelIndex];
            }
            // OCR3A = stepperTest.OCRarray[stepperTest.accelDecelIndex];
        }
        if ((stepperTest.incrementSteps > 0)&&(!(stepperTest.stepsTaken % stepperTest.speedUpdateSteps))&&(stepperTest.stepsTaken >= (stepperTest.incrementSteps - stepperTest.decelSteps)))
        { // deceleration phase - targeted moves only
            if (stepperTest.OCRarray[stepperTest.accelDecelIndex] <= stepperTest.OCRmin)
            {
                OCR3A = stepperTest.OCRmin;
            }
            else
            {
                OCR3A = stepperTest.OCRarray[stepperTest.accelDecelIndex];
            }
            // OCR3A = stepperTest.OCRarray[stepperTest.accelDecelIndex];
            stepperTest.accelDecelIndex--;
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM