/* * nzs.cpp * * Created on: Dec 8, 2016 * Author: trampas * 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 . 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 "nzs.h" #include "commands.h" #include "nonvolatile.h" #include "angle.h" #include "eeprom.h" #include "steppin.h" #include "wiring_private.h" #pragma GCC push_options #pragma GCC optimize ("-Ofast") eepromData_t PowerupEEPROM={0}; volatile bool enableState=true; int32_t dataEnabled=0; StepperCtrl stepperCtrl; LCD Lcd; int menuCalibrate(int argc, char *argv[]) { stepperCtrl.calibrateEncoder(); } int menuTestCal(int argc, char *argv[]) { Angle error; int32_t x,y; char str[25]; error=stepperCtrl.maxCalibrationError(); x=(36000*(int32_t)error)/ANGLE_STEPS; LOG("Error %d %d", (int32_t)error, x); y=x/100; x=x-(y*100); x=abs(x); sprintf(str, "%d.%02d deg",y,x); #ifndef DISABLE_LCD Lcd.lcdShow("Cal Error", str,""); #endif LOG("Calibration error %s",str); #ifndef MECHADUINO_HARDWARE while(digitalRead(PIN_SW3)==1) { //wait for button press } while(digitalRead(PIN_SW3)==0) { //wait for button release } #endif } static options_t stepOptions[] { {"200"}, {"400"}, {""}, }; //returns the index of the stepOptions when called // with no arguments. int motorSteps(int argc, char *argv[]) { if (argc==0) { int i; i=NVM->motorParams.fullStepsPerRotation; if (i==400) { return 1; } return 0; //default to 200 } if (argc>0) { int32_t i; MotorParams_t params; memcpy((void *)¶ms, (void *)&NVM->motorParams, sizeof(params)); i=atol(argv[0]); if (i!=params.fullStepsPerRotation) { params.fullStepsPerRotation=i; nvmWriteMotorParms(params); } } return 0; } static options_t currentOptions[] { {"0"}, {"100"}, {"200"}, {"300"}, {"400"}, {"500"}, {"600"}, {"700"}, {"800"}, {"900"}, {"1000"}, {"1100"}, {"1200"}, {"1300"}, {"1400"}, {"1500"}, {"1600"}, {"1700"}, {"1800"}, {"1900"}, {"2000"}, {"2100"}, {"2200"}, {"2300"}, {"2400"}, {"2500"}, {"2600"}, {"2700"}, {"2800"}, {"2900"}, {"3000"}, {"3100"}, {"3200"}, {"3300"}, {""}, }; int motorCurrent(int argc, char *argv[]) { LOG("called motorCurrent %d",argc); if (argc==1) { int i; LOG("called %s",argv[0]); i=atol(argv[0]); i=i*100; MotorParams_t params; memcpy((void *)¶ms, (void *)&NVM->motorParams, sizeof(params)); if (i!=params.currentMa) { params.currentMa=i; nvmWriteMotorParms(params); } return i/100; } int i; i=NVM->motorParams.currentMa/100; LOG(" motorCurrent return %d",i); return i; } int motorHoldCurrent(int argc, char *argv[]) { if (argc==1) { int i; i=atol(argv[0]); i=i*100; MotorParams_t params; memcpy((void *)¶ms, (void *)&NVM->motorParams, sizeof(params)); if (i!=params.currentHoldMa) { params.currentHoldMa=i; nvmWriteMotorParms(params); } return i/100; }else { int i; i=NVM->motorParams.currentHoldMa/100; return i; } } static options_t microstepOptions[] { {"1"}, {"2"}, {"4"}, {"8"}, {"16"}, {"32"}, {"64"}, {"128"}, {"256"}, {""} }; int microsteps(int argc, char *argv[]) { if (argc==1) { int i,steps; i=atol(argv[0]); SystemParams_t params; memcpy((void *)¶ms, (void *)&NVM->SystemParams, sizeof(params)); steps=0x01<SystemParams.microsteps; for (j=0; j<9; j++) { if ((0x01<SystemParams, sizeof(params)); if (i!=params.controllerMode) { params.controllerMode=(feedbackCtrl_t)i; nvmWriteSystemParms(params); } return i; } return NVM->SystemParams.controllerMode; } #ifndef PIN_ENABLE static options_t errorPinOptions[] { {"Enable"}, {"!Enable"}, //error pin works like enable on step sticks {"Error"}, // {"BiDir"}, //12/12/2016 not implemented yet {""} }; int errorPin(int argc, char *argv[]) { if (argc==1) { int i; i=atol(argv[0]); SystemParams_t params; memcpy((void *)¶ms, (void *)&NVM->SystemParams, sizeof(params)); if (i!=params.errorPinMode) { params.errorPinMode=(ErrorPinMode_t)i; nvmWriteSystemParms(params); } return i; } return NVM->SystemParams.errorPinMode; } #else static options_t errorPinOptions[] { {"Enable"}, {"!Enable"}, //error pin works like enable on step sticks // {"Error"}, // {"BiDir"}, //12/12/2016 not implemented yet {""} }; int enablePin(int argc, char *argv[]) { if (argc==1) { int i; i=atol(argv[0]); SystemParams_t params; memcpy((void *)¶ms, (void *)&NVM->SystemParams, sizeof(params)); if (i!=params.errorPinMode) { params.errorPinMode=(ErrorPinMode_t)i; nvmWriteSystemParms(params); } return i; } return NVM->SystemParams.errorPinMode; } #endif static options_t dirPinOptions[] { {"High CW"}, {"High CCW"}, {""} }; int dirPin(int argc, char *argv[]) { if (argc==1) { int i; i=atol(argv[0]); SystemParams_t params; memcpy((void *)¶ms, (void *)&NVM->SystemParams, sizeof(params)); if (i!=params.dirPinRotation) { params.dirPinRotation=(RotationDir_t)i; nvmWriteSystemParms(params); } return i; } return NVM->SystemParams.dirPinRotation; } static menuItem_t MenuMain[] { {"Calibrate", menuCalibrate,NULL}, {"Test Cal", menuTestCal,NULL}, // {"Mtr steps", motorSteps,stepOptions}, NOT GOOD for user to call this {"Motor mA", motorCurrent,currentOptions}, {"Hold mA", motorHoldCurrent,currentOptions}, {"Microstep", microsteps,microstepOptions}, // {"Ctlr Mode", controlLoop,controlLoopOptions}, //this may not be good for user to call #ifndef PIN_ENABLE {"Error Pin", errorPin,errorPinOptions}, #else {"EnablePin", enablePin,errorPinOptions}, #endif {"Dir Pin", dirPin,dirPinOptions}, { "", NULL} }; static menuItem_t MenuCal[] { {"Calibrate", menuCalibrate,NULL}, //{"Test Cal", menuTestCal,NULL}, { "", NULL} }; //this function is called when error pin changes as enable signal static void enableInput(void) { static bool lastState=true; #ifdef PIN_ENABLE if (NVM->SystemParams.errorPinMode == ERROR_PIN_MODE_ENABLE) { static int enable; //read our enable pin enable = digitalRead(PIN_ENABLE); if (enable != enableState) { WARNING("Enable now %d",enable); } enableState=enable; //stepperCtrl.enable(enable); } if (NVM->SystemParams.errorPinMode == ERROR_PIN_MODE_ACTIVE_LOW_ENABLE) { static int enable; //read our enable pin enable = !digitalRead(PIN_ENABLE); if (enable != enableState) { WARNING("Enable now %d",enable); } enableState=enable; //stepperCtrl.enable(enable); } #else if (NVM->SystemParams.errorPinMode == ERROR_PIN_MODE_ENABLE) { static int enable; //read our enable pin enable = digitalRead(PIN_ERROR); enableState=enable; //stepperCtrl.enable(enable); } if (NVM->SystemParams.errorPinMode == ERROR_PIN_MODE_ACTIVE_LOW_ENABLE) { static int enable; //read our enable pin enable = !digitalRead(PIN_ERROR); enableState=enable; //stepperCtrl.enable(enable); } #endif #ifdef USE_STEP_DIR_SERIAL static uint8_t pinCFG[2]; static uint8_t pinMux[2]; if (enableState == false && lastState==true) { // turn the step/dir to serial port //save pin config for restoring pinCFG[0]=getPinCfg(PIN_STEP_INPUT); pinCFG[1]=getPinCfg(PIN_DIR_INPUT); pinMux[0]=getPinMux(PIN_STEP_INPUT); pinMux[1]=getPinMux(PIN_DIR_INPUT); //lets see if the step pin has interrupt enabled if (pinMux[0] == PORT_PMUX_PMUXE_A_Val) { EExt_Interrupts in = g_APinDescription[PIN_STEP_INPUT].ulExtInt; EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT(1 << in); //disable the interrupt //we need to disable the interrupt } //now we need to set the pins to serial port peripheral (sercom0) setPinMux(PIN_STEP_INPUT,PORT_PMUX_PMUXE_C_Val); setPinMux(PIN_DIR_INPUT,PORT_PMUX_PMUXE_C_Val); //make sure that step pin is input with mux to peripheral setPinCfg(PIN_STEP_INPUT, PORT_PINCFG_PMUXEN | PORT_PINCFG_INEN | PORT_PINCFG_PULLEN); //make sure that dir pin is an output with mux to peripheral setPinCfg(PIN_DIR_INPUT, PORT_PINCFG_PMUXEN ); Serial1.begin(STEP_DIR_BAUD); } if (enableState == true && lastState==false) { Serial1.end(); setPinMux(PIN_STEP_INPUT,pinMux[0]); setPinMux(PIN_DIR_INPUT,pinMux[1]); setPinCfg(PIN_STEP_INPUT,pinCFG[0]); setPinCfg(PIN_DIR_INPUT,pinCFG[1]); //turn step/dir pins back to GPIO if (PORT_PMUX_PMUXE_A_Val == pinMux[0]) { //if interrupt was enabled for step pin renable it. EExt_Interrupts in = g_APinDescription[PIN_STEP_INPUT].ulExtInt; EIC->INTENSET.reg = EIC_INTENCLR_EXTINT(1 << in); //enable the interrupt } } #endif //USE_STEP_DIR_SERIAL lastState=enableState; } void TC5_Handler() { // static bool led=false; // YELLOW_LED(led); // led=!led; interrupts(); //allow other interrupts if (TC5->COUNT16.INTFLAG.bit.OVF == 1) { int error=0; error=(stepperCtrl.processFeedback()); //handle the control loop YELLOW_LED(error); #ifdef PIN_ENABLE GPIO_OUTPUT(PIN_ERROR); bool level; level = !NVM->SystemParams.errorLogic; if (error) { //assume high is inactive and low is active on error pin digitalWrite(PIN_ERROR,level); }else { digitalWrite(PIN_ERROR,!level); } #else if (NVM->SystemParams.errorPinMode == ERROR_PIN_MODE_ERROR) { GPIO_OUTPUT(PIN_ERROR); if (error) { //assume high is inactive and low is active on error pin digitalWrite(PIN_ERROR,LOW); }else { digitalWrite(PIN_ERROR,HIGH); } } #endif TC5->COUNT16.INTFLAG.bit.OVF = 1; // writing a one clears the flag ovf flag } } //check the NVM and set to defaults if there is any void validateAndInitNVMParams(void) { if (false == NVM->sPID.parametersVaild) { nvmWrite_sPID(0.9,0.0001, 0.01); } if (false == NVM->pPID.parametersVaild) { nvmWrite_pPID(1.0, 0, 0); } if (false == NVM->vPID.parametersVaild) { nvmWrite_vPID(2.0, 1.0, 1.0); } if (false == NVM->SystemParams.parametersVaild) { SystemParams_t params; params.microsteps=16; #if defined(CTRL_POS_PID_AS_DEFAULT) params.controllerMode=CTRL_POS_PID; #else params.controllerMode=CTRL_SIMPLE; #endif params.dirPinRotation=CW_ROTATION; //default to clockwise rotation when dir is high params.errorLimit=(int32_t)ANGLE_FROM_DEGREES(1.8); params.errorPinMode=ERROR_PIN_MODE_ENABLE; //default to enable pin params.homePin=-1; params.errorLogic=false; params.homeAngleDelay=ANGLE_FROM_DEGREES(10); nvmWriteSystemParms(params); } //the motor parameters are check in the stepper_controller code // as that there we can auto set much of them. } void SYSCTRL_Handler(void) { if (SYSCTRL->INTFLAG.reg & SYSCTRL_INTFLAG_BOD33DET) { eepromFlush(); //flush the eeprom SYSCTRL->INTFLAG.reg |= SYSCTRL_INTFLAG_BOD33DET; } } // Wait for synchronization of registers between the clock domains static __inline__ void syncBOD33(void) __attribute__((always_inline, unused)); static void syncBOD33(void) { //int32_t t0=1000; while (SYSCTRL->PCLKSR.bit.BOD33RDY==1) { // t0--; // if (t0==0) // { // break; // } } } static void configure_bod(void) { //syncBOD33(); //SYSCTRL->BOD33.reg=0; //disable BOD33 before starting //syncBOD33(); SYSCTRL->BOD33.reg=SYSCTRL_BOD33_ACTION_INTERRUPT | //generate interrupt when BOD is triggered SYSCTRL_BOD33_LEVEL(48) | //about 3.2V //SYSCTRL_BOD33_HYST | //enable hysteresis SYSCTRL_BOD33_ENABLE; //turn module on LOG("BOD33 %02X", SYSCTRL->BOD33.reg ); SYSCTRL->INTENSET.reg |= SYSCTRL_INTENSET_BOD33DET; NVIC_SetPriority(SYSCTRL_IRQn, 1); //make highest priority as we need to save eeprom // Enable InterruptVector NVIC_EnableIRQ(SYSCTRL_IRQn); } void NZS::begin(void) { int to=20; stepCtrlError_t stepCtrlError; //set up the pins correctly on the board. boardSetupPins(); //start up the USB serial interface //TODO check for power on USB before doing this... #ifndef MECHADUINO_HARDWARE SerialUSB.begin(SERIAL_BAUD); #endif //setup the serial port for syslog Serial5.begin(SERIAL_BAUD); #ifndef CMD_SERIAL_PORT SysLogInit(&Serial5,LOG_DEBUG); pinPeripheral(PIN_TXD, PIO_SERCOM_ALT); pinPeripheral(PIN_RXD, PIO_SERCOM_ALT); #else SysLogInit(NULL, LOG_WARNING); #endif LOG("Power up!"); pinMode(PIN_USB_PWR, INPUT); #ifndef MECHADUINO_HARDWARE if (digitalRead(PIN_USB_PWR)) { //wait for USB serial port to come alive while (!SerialUSB) { to--; if (to == 0) { break; } delay(500); }; //wait for serial } else { WARNING("USB Not connected"); } #endif validateAndInitNVMParams(); LOG("EEPROM INIT"); if (EEPROM_OK == eepromInit()) //init the EEPROM { eepromRead((uint8_t *)&PowerupEEPROM, sizeof(PowerupEEPROM)); } configure_bod(); //configure the BOD #ifndef DISABLE_LCD LOG("Testing LCD"); Lcd.begin(&stepperCtrl); #ifdef A5995_DRIVER Lcd.lcdShow("MisfitTech","NEMA 23", VERSION); #else Lcd.lcdShow("MisfitTech","NEMA 17", VERSION); #endif #endif LOG("command init!"); commandsInit(); //setup command handler system stepCtrlError=STEPCTRL_NO_CAL; while (STEPCTRL_NO_ERROR != stepCtrlError) { LOG("init the stepper controller"); stepCtrlError=stepperCtrl.begin(); //start controller before accepting step inputs //todo we need to handle error on LCD and through command line if (STEPCTRL_NO_POWER == stepCtrlError) { #ifndef MECHADUINO_HARDWARE SerialUSB.println("Appears that there is no Motor Power"); SerialUSB.println("Connect motor power!"); #else Serial5.println("Appears that there is no Motor Power"); Serial5.println("Connect motor power!"); #endif #ifndef DISABLE_LCD Lcd.lcdShow("Waiting", "MOTOR", "POWER"); #endif while (STEPCTRL_NO_POWER == stepCtrlError) { stepCtrlError=stepperCtrl.begin(); //start controller before accepting step inputs } } if (STEPCTRL_NO_CAL == stepCtrlError) { #ifndef MECHADUINO_HARDWARE SerialUSB.println("You need to Calibrate"); #else Serial5.println("You need to Calibrate"); #endif #ifndef DISABLE_LCD Lcd.lcdShow(" NOT ", "Calibrated", " "); delay(1000); Lcd.setMenu(MenuCal); Lcd.forceMenuActive(); #endif //TODO add code here for LCD and command line loop while(false == stepperCtrl.calibrationValid()) { commandsProcess(); //handle commands #ifndef DISABLE_LCD Lcd.process(); #endif } #ifndef DISABLE_LCD Lcd.setMenu(NULL); #endif } if (STEPCTRL_NO_ENCODER == stepCtrlError) { #ifndef MECHADUINO_HARDWARE SerialUSB.println("AS5047D not working"); SerialUSB.println(" try disconnecting power from board for 15+mins"); SerialUSB.println(" you might have to short out power pins to ground"); #else Serial5.println("AS5047D not working"); Serial5.println(" try disconnecting power from board for 15+mins"); Serial5.println(" you might have to short out power pins to ground"); #endif #ifndef DISABLE_LCD Lcd.lcdShow("Encoder", " Error!", " REBOOT"); #endif while(1) { } } } #ifndef DISABLE_LCD Lcd.setMenu(MenuMain); #endif stepPinSetup(); //setup the step pin #ifdef PIN_ENABLE //attachInterrupt(digitalPinToInterrupt(PIN_ENABLE), enableInput, CHANGE); NVIC_SetPriority(EIC_IRQn, 0); //set port A interrupt as highest priority #else attachInterrupt(digitalPinToInterrupt(PIN_ERROR), enableInput, CHANGE); #endif SmartPlanner.begin(&stepperCtrl); RED_LED(false); LOG("SETUP DONE!"); } void printLocation(void) { char buf[128]={0}; Location_t loc; int32_t n, i, len; int32_t pktSize; if (dataEnabled==0) { //RED_LED(false); return; } //the packet length for binary print is 12bytes // assuming rate of 6Khz this would be 72,000 baud i=0; n=stepperCtrl.getLocation(&loc); if (n==-1) { //RED_LED(false); return; } len=0; pktSize=sizeof(Location_t)+1; //packet lenght is size location plus sync byte // //binary write while(n>=0 && (len)<=(128-pktSize)) { memcpy(&buf[len],&loc,sizeof(Location_t)); len+=sizeof(Location_t); buf[len]=0XAA; //sync len++; buf[len]=sizeof(Location_t); //data len len++; n=stepperCtrl.getLocation(&loc); i++; } #ifndef MECHADUINO_HARDWARE SerialUSB.write(buf,len); #endif //hex write // hex write is 29 bytes per tick, @ 6khz this 174000 baud // while(n>=0 && (i*29)<(200-29)) // { // sprintf(buf,"%s%08X\t%08X\t%08X\n\r",buf,loc.microSecs,loc.desiredLoc,loc.actualLoc); // n=stepperCtrl.getLocation(&loc); // i++; // } // SerialUSB.write(buf,strlen(buf)); // if (n<=0) // { // RED_LED(false); // }else // { // RED_LED(true); // } return; } void NZS::loop(void) { eepromData_t eepromData; // if (dataEnabled==0) // { // LOG("loop time is %dus",stepperCtrl.getLoopTime()); // } //read the enable pin and update // this is also done as an edge interrupt but does not always see // to trigger the ISR. enableInput(); if (enableState != stepperCtrl.getEnable()) { stepperCtrl.enable(enableState); } //handle EEPROM eepromData.angle=stepperCtrl.getCurrentAngle(); eepromData.encoderAngle=stepperCtrl.getEncoderAngle(); eepromData.valid=1; eepromWriteCache((uint8_t *)&eepromData,sizeof(eepromData)); commandsProcess(); //handle commands #ifndef DISABLE_LCD Lcd.process(); #endif //stepperCtrl.PrintData(); //prints steps and angle to serial USB. printLocation(); //print out the current location return; } #pragma GCC pop_options