Trikarus/firmware_smartstepper_trikarus/stepper_nano_zero/steppin.cpp
2020-07-20 22:21:36 +02:00

315 lines
7.6 KiB
C++

/*
* Copyright (C) 2018 MisfitTech, All rights reserved.
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.
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 <https://www.gnu.org/licenses/>.
Written by Trampas Stern for MisfitTech.
Misfit Tech invests time and resources providing this open source code,
please support MisfitTech and open-source hardware by purchasing
products from MisfitTech, www.misifittech.net!
*/
#include "steppin.h"
#include "stepper_controller.h"
#include "wiring_private.h"
#include "Arduino.h"
extern StepperCtrl stepperCtrl;
volatile int32_t stepsChanged=0;
volatile int64_t steps=0;
#if (PIN_STEP_INPUT != 0)
#error "this code only works with step pin being D0 (PA11, EXTINT11)"
#endif
#define WAIT_TCC2_SYNC() while(TCC2->SYNCBUSY.reg)
void checkDir(void)
{
int dir=1;
static int lastDir=-1;
if (CW_ROTATION == NVM->SystemParams.dirPinRotation)
{
dir=0; //reverse the rotation
}
if (lastDir != dir)
{
if (dir)
{
EIC->CONFIG[1].reg &= ~EIC_CONFIG_SENSE2_Msk;
EIC->CONFIG[1].reg |= EIC_CONFIG_SENSE2_HIGH;
} else
{
EIC->CONFIG[1].reg &= ~EIC_CONFIG_SENSE2_Msk;
EIC->CONFIG[1].reg |= EIC_CONFIG_SENSE2_LOW;
}
lastDir=dir;
}
}
//this function can not be called in interrupt context as the overflow interrupt for tC4 needs to run.
int64_t getSteps(void)
{
//#ifndef USE_NEW_STEP
// return 0;
//#endif
int64_t x;
#ifdef USE_TC_STEP
uint16_t y;
static uint16_t lasty=0;
TCC2->CTRLBSET.reg=TCC_CTRLBSET_CMD_READSYNC;
WAIT_TCC2_SYNC();
y=(uint16_t)(TCC2->COUNT.reg & 0x0FFFFul); //use only lowest 16bits
//LOG("count is %d",y);
steps += (int16_t)(y-lasty);
lasty=y;
checkDir();
return steps;
#else
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT11;
x=stepsChanged;
stepsChanged=0;
EIC->INTENSET.reg = EIC_INTENSET_EXTINT11;
return x;
#endif
}
//this function is called on the rising edge of a step from external device
static void stepInput(void)
{
static int dir;
//read our direction pin
dir = digitalRead(PIN_DIR_INPUT);
if (CW_ROTATION == NVM->SystemParams.dirPinRotation)
{
dir=!dir; //reverse the rotation
}
#ifndef USE_NEW_STEP
stepperCtrl.requestStep(dir,1);
#else
if (dir)
{
stepsChanged++;
}else
{
stepsChanged--;
}
#endif
}
void enableEIC(void)
{
PM->APBAMASK.reg |= PM_APBAMASK_EIC;
if (EIC->CTRL.bit.ENABLE == 0)
{
// Enable GCLK for IEC (External Interrupt Controller)
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_EIC));
// Enable EIC
EIC->CTRL.bit.ENABLE = 1;
while (EIC->STATUS.bit.SYNCBUSY == 1) { }
}
}
void setupStepEvent(void)
{
//we will set up the EIC to generate an even on rising edge of step pin
//make sure EIC is setup
enableEIC();
// Assign step pin to EIC
// Step pin is PA11, EXTINT11
pinPeripheral(PIN_STEP_INPUT, PIO_EXTINT);
//set up the direction pin PA10 to trigger external interrupt
pinPeripheral(PIN_DIR_INPUT, PIO_EXTINT); //EXTINT10
//***** setup EIC ******
EIC->EVCTRL.bit.EXTINTEO11=1; //enable event for EXTINT11
EIC->EVCTRL.bit.EXTINTEO10=1; //enable event for EXTINT10
//setup up external interurpt 11 to be rising edge triggered
//setup up external interurpt 10 to be both edge triggered
EIC->CONFIG[1].reg |= EIC_CONFIG_SENSE3_RISE | EIC_CONFIG_SENSE2_HIGH;
checkDir();
//disable actually generating an interrupt, we only want event triggered
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT11;
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT10;
//**** setup the event system ***
// Enable GCLK for EVSYS channel 0
PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_EVSYS_CHANNEL_0));
while (GCLK->STATUS.bit.SYNCBUSY);
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_EVSYS_CHANNEL_1));
while (GCLK->STATUS.bit.SYNCBUSY);
//setup the step pin to trigger event 0 on the TCC2 (step)
EVSYS->CHANNEL.reg=EVSYS_CHANNEL_CHANNEL(0)
| EVSYS_CHANNEL_EDGSEL_RISING_EDGE
| EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_11)
| EVSYS_CHANNEL_PATH_ASYNCHRONOUS;
EVSYS->USER.reg = EVSYS_USER_CHANNEL(1)
| EVSYS_USER_USER(EVSYS_ID_USER_TCC2_EV_0);
//setup the dir pin to trigger event 2 on the TCC2 (dir change)
EVSYS->CHANNEL.reg=EVSYS_CHANNEL_CHANNEL(1)
| EVSYS_CHANNEL_EDGSEL_BOTH_EDGES
| EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_10)
| EVSYS_CHANNEL_PATH_ASYNCHRONOUS;
EVSYS->USER.reg = EVSYS_USER_CHANNEL(2)
| EVSYS_USER_USER(EVSYS_ID_USER_TCC2_EV_1);
//**** setup the Timer counter ******
PM->APBCMASK.reg |= PM_APBCMASK_TCC2;
// Enable GCLK for TCC2 (timer counter input clock)
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TCC2_TC3));
while (GCLK->STATUS.bit.SYNCBUSY);
TCC2->CTRLA.reg &= ~TCC_CTRLA_ENABLE;
WAIT_TCC2_SYNC();
TCC2->CTRLA.reg= TCC_CTRLA_SWRST; //reset TCC2
WAIT_TCC2_SYNC();
while(TCC2->CTRLA.bit.SWRST ==1);
//TCC2->WAVE.reg = TCC_WAVE_WAVEGEN_NFRQ;
//WAIT_TCC2_SYNC();
TCC2->EVCTRL.reg=TCC_EVCTRL_EVACT0_COUNTEV | TCC_EVCTRL_TCEI0
| TCC_EVCTRL_EVACT1_DIR | TCC_EVCTRL_TCEI1;
WAIT_TCC2_SYNC();
TCC2->COUNT.reg=0;
WAIT_TCC2_SYNC();
//TCC2->CTRLBSET.bit.CMD=TCC_CTRLBSET_CMD_RETRIGGER;
//checkDirPin();
TCC2->CTRLBSET.bit.DIR=1;
WAIT_TCC2_SYNC();
TCC2->CTRLA.reg |=TCC_CTRLA_ENABLE;
WAIT_TCC2_SYNC();
//checkDirPin();
//
// TC4->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 // Set Timer counter Mode to 16 bits
// | TC_CTRLA_WAVEGEN_NFRQ //normal counting mode (not using waveforms)
// | TC_CTRLA_PRESCALER_DIV1; //count each pulse
// WAIT_TC32_REGS_SYNC(TC4)
//
// TC4->COUNT16.CTRLBCLR.reg=0xFF; //clear all values.
// WAIT_TC32_REGS_SYNC(TC4)
//
// TC4->COUNT16.EVCTRL.reg=TC_EVCTRL_TCEI | TC_EVCTRL_EVACT_COUNT; //enable event input and count
// WAIT_TC32_REGS_SYNC(TC4)
//
// TC4->COUNT16.COUNT.reg=0;
// WAIT_TC32_REGS_SYNC(TC4)
//
// TC4->COUNT16.INTENSET.bit.OVF = 1; //enable over/under flow interrupt
// //setup the TC overflow/underflow interrupt
// NVIC_SetPriority(TC4_IRQn, 0);
// // Enable InterruptVector
// NVIC_EnableIRQ(TC4_IRQn);
//
//
// // Enable TC
// TC4->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
// WAIT_TC32_REGS_SYNC(TC4)
}
//static void dirChanged_ISR(void)
//{
// int dir=0;
// //read our direction pin
// //dir = digitalRead(PIN_DIR_INPUT);
// if ( (PORT->Group[g_APinDescription[PIN_DIR_INPUT].ulPort].IN.reg & (1ul << g_APinDescription[PIN_DIR_INPUT].ulPin)) != 0 )
// {
// dir=1;
// }
//
//
// if (CW_ROTATION == NVM->SystemParams.dirPinRotation)
// {
// dir=!dir; //reverse the rotation
// }
// if (dir)
// {
// TC4->COUNT16.CTRLBSET.bit.DIR=1;
// } else
// {
// TC4->COUNT16.CTRLBCLR.bit.DIR=1;
// }
//}
void stepPinSetup(void)
{
#ifdef USE_TC_STEP
// //setup the direction pin
// dirChanged_ISR();
//
// //attachInterrupt configures the EIC as highest priority interrupts.
// attachInterrupt(digitalPinToInterrupt(PIN_DIR_INPUT), dirChanged_ISR, CHANGE);
setupStepEvent();
// NVIC_SetPriority(EIC_IRQn, 1); //set port A interrupt as highest priority
#else
attachInterrupt(digitalPinToInterrupt(PIN_STEP_INPUT), stepInput, RISING);
NVIC_SetPriority(EIC_IRQn, 0); //set port A interrupt as highest priority
#endif
}