diff options
author | David Crocker <dcrocker@eschertech.com> | 2018-08-29 20:32:40 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2018-08-29 20:32:40 +0300 |
commit | 06335b899c408cdf1539425b9d918f43d5ccfbdc (patch) | |
tree | 90e4c7a217374167747185ff3d4440c0d78c2c65 /src | |
parent | 413586e519ce77215d9bee808cc62cf0e6ac6f79 (diff) |
More work towards 2.02beta2
Duet Maestro 12864 display: merged in Matt's changes and did some fixes
Changed Smart Drivers interface to support accessing additional registers and parts of registers
Started implementing an object model
Change C++ compilation mode to gnu++17 to handle lambda function pointers in flash memory
Added NamedEnum class *(notuzsed yet)
Diffstat (limited to 'src')
-rw-r--r-- | src/BugList.txt | 14 | ||||
-rw-r--r-- | src/Display/Menu.cpp | 127 | ||||
-rw-r--r-- | src/Display/Menu.h | 9 | ||||
-rw-r--r-- | src/GCodes/DriverMode.h | 27 | ||||
-rw-r--r-- | src/GCodes/GCodes.cpp | 27 | ||||
-rw-r--r-- | src/GCodes/GCodes.h | 11 | ||||
-rw-r--r-- | src/GCodes/GCodes2.cpp | 8 | ||||
-rw-r--r-- | src/Libraries/General/SafeVsnprintf.cpp | 2 | ||||
-rw-r--r-- | src/Movement/StepperDrivers/DriverMode.cpp (renamed from src/GCodes/DriverMode.cpp) | 0 | ||||
-rw-r--r-- | src/Movement/StepperDrivers/DriverMode.h | 42 | ||||
-rw-r--r-- | src/Movement/StepperDrivers/TMC22xx.cpp | 140 | ||||
-rw-r--r-- | src/Movement/StepperDrivers/TMC22xx.h | 9 | ||||
-rw-r--r-- | src/Movement/StepperDrivers/TMC2660.cpp | 163 | ||||
-rw-r--r-- | src/Movement/StepperDrivers/TMC2660.h | 9 | ||||
-rw-r--r-- | src/Movement/StepperDrivers/TMC51xx.cpp | 50 | ||||
-rw-r--r-- | src/Movement/StepperDrivers/TMC51xx.h | 7 | ||||
-rw-r--r-- | src/NamedEnum.h | 53 | ||||
-rw-r--r-- | src/ObjectModel/ObjectModel.cpp | 308 | ||||
-rw-r--r-- | src/ObjectModel/ObjectModel.h | 160 | ||||
-rw-r--r-- | src/Platform.cpp | 2 | ||||
-rw-r--r-- | src/RepRap.cpp | 27 | ||||
-rw-r--r-- | src/RepRap.h | 12 | ||||
-rw-r--r-- | src/Version.h | 2 |
23 files changed, 937 insertions, 272 deletions
diff --git a/src/BugList.txt b/src/BugList.txt index c8fa66b8..635846bf 100644 --- a/src/BugList.txt +++ b/src/BugList.txt @@ -28,12 +28,24 @@ Implemented in 2.02beta1: - PCCB build: endstop 0 and 1 are now assigned to Z and X respectively - added check for timeout when sending to the W5500 but not receiving +Investigations: +- Weird height map, https://forum.duet3d.com/topic/6472/mesh-grid-compensation/28 +- Full stepping problem, https://forum.duet3d.com/topic/6433/how-to-reduce-the-speed-of-extrusion/14 +- Print time estimation problem, see https://forum.duet3d.com/topic/5572/it-s-out-reprapfirmware-2-0-and-1-21-1-released/64 +- Disconnections, https://forum.duet3d.com/topic/6487/running-bed-mesh-compensation/19 + Remaining: - [done, ok on DuetM, test on Duet2] No warning messages when TMC2224 drivers overheat, https://forum.duet3d.com/topic/6309/little-monster-s-hort-to-ground/13 +- [done] Different numbers of endstop inputs and motor drivers +- Check M106 R1 parameter, see https://forum.duet3d.com/topic/6538/resuming-print-fan-after-pause +- Support mixed stealthchop/spread cycle mode on Maestro via TPWMTHRS register, https://forum.duet3d.com/topic/6512/duet-2-maestro-stealthchop-default/7 +- Add check digit to serial number +- check DAA working as intended, results are inconsistent +- chrishamm's watchdog issue, see his email oif 2018-08-01 - G30 S-1 when no Z probe, https://forum.duet3d.com/topic/6510/a-couple-of-dwc-odd-things/2 - Command to copy output from the following commands to the log file? - Track which devices use which pins -- Matt's display changes/other display fixes +- [done, M3D testing] Merge in Matt's display changes - Hangprinter PRs - PR to have any enabled driver report 50C - PR to allow endstop input number to be specified in M558 diff --git a/src/Display/Menu.cpp b/src/Display/Menu.cpp index d530f548..1a30cc99 100644 --- a/src/Display/Menu.cpp +++ b/src/Display/Menu.cpp @@ -21,7 +21,7 @@ * Wnn is the width in pixels for the element * Dnn specifies the number of decimal places for numeric display * - * Vnn specifies the item's visibility with value: + * Vnn specifies the item's visibility (currently implemented for buttons only) with value: * 0 always visible (default if not specified) * 2 visible when the printer is actively printing (actively printing defined as not paused, pausing or resuming) * 3 visible when the printer is NOT actively printing @@ -31,6 +31,8 @@ * 7 visible when the printer is printing and NOT in paused state (actively printing or resuming) * 10 visible when SD card 0 is mounted * 11 visible when SD card 0 is NOT mounted + * 20 visible when the current or default tool has a temperature fault + * 28 visible when the bed heater has a temperature fault * * "action" can be any of: * - a Gcode command string (must begin with G, M or T). In such a string, #0 represents the full name of the current file, in double quotes, set when a file is selected @@ -74,11 +76,14 @@ #include "Storage/MassStorage.h" #include "Tools/Tool.h" +const uint32_t InactivityTimeout = 20000; // inactivity timeout +const uint32_t ErrorTimeout = 6000; // how long wre display an error message for + Menu::Menu(Lcd7920& refLcd, const LcdFont * const fnts[], size_t nFonts) : lcd(refLcd), fonts(fnts), numFonts(nFonts), - timeoutEnabled(false), lastActionTime(millis()), - selectableItems(nullptr), unSelectableItems(nullptr), numNestedMenus(0), numSelectableItems(0), highlightedItem(0), itemIsSelected(false), - rowOffset(0) + timeoutValue(0), lastActionTime(0), + selectableItems(nullptr), unSelectableItems(nullptr), numNestedMenus(0), numSelectableItems(0), highlightedItem(0), itemIsSelected(false), displayingFixedMenu(false), + errorColumn(0), rowOffset(0) { } @@ -111,56 +116,46 @@ void Menu::Load(const char* filename) } ++numNestedMenus; + displayingFixedMenu = false; Reload(); } } void Menu::LoadFixedMenu() { - if (numNestedMenus < MaxMenuNesting) - { - filenames[numNestedMenus].copy(m_pcFixedMenu); - - rowOffset = 0; - - currentMargin = 0; - lcd.Clear(); - - ++numNestedMenus; - - // Instead of Reload(): - lcd.SetRightMargin(NumCols - currentMargin); + displayingFixedMenu = true; + numNestedMenus = 0; + rowOffset = 0; + currentMargin = 0; + lcd.Clear(); - ResetCache(); + // Instead of Reload(): + lcd.SetRightMargin(NumCols - currentMargin); - char acLine1[] = "text R3 C5 F0 T\"No SD Card Found\""; - char acLine2[] = "button R15 C5 F0 T\"Mount SD\" A\"M21\""; + ResetCache(); - const char *errMsg = ParseMenuLine(acLine1); - if (nullptr != errMsg) - { - LoadError(errMsg, 1); - } - if (commandBufferIndex == sizeof(commandBuffer)) - { - LoadError("|Menu buffer full", 1); - } + char acLine1[] = "text R3 C5 F0 T\"No SD Card Found\""; + char acLine2[] = "button R15 C5 F0 T\"Mount SD\" A\"M21\""; - errMsg = ParseMenuLine(acLine2); - if (nullptr != errMsg) - { - LoadError(errMsg, 2); - } - if (commandBufferIndex == sizeof(commandBuffer)) - { - LoadError("|Menu buffer full", 2); - } + const char *errMsg = ParseMenuLine(acLine1); + if (nullptr != errMsg) + { + LoadError(errMsg, 1); + } + if (commandBufferIndex == sizeof(commandBuffer)) + { + LoadError("|Menu buffer full", 1); } -} -bool Menu::bInFixedMenu() const -{ - return ((0 != numNestedMenus) && (0 == strcmp(filenames[numNestedMenus - 1].c_str(), m_pcFixedMenu))); + errMsg = ParseMenuLine(acLine2); + if (nullptr != errMsg) + { + LoadError(errMsg, 2); + } + if (commandBufferIndex == sizeof(commandBuffer)) + { + LoadError("|Menu buffer full", 2); + } } void Menu::Pop() @@ -179,26 +174,31 @@ void Menu::LoadError(const char *msg, unsigned int line) lcd.Clear(currentMargin, currentMargin, NumRows - currentMargin, NumCols - currentMargin); lcd.SetFont(fonts[0]); - lcd.print("Error loading menu\nFile "); - lcd.print(filenames[numNestedMenus - 1].c_str()); + lcd.print("Error loading menu\nFile: "); + lcd.print((numNestedMenus > 0) ? filenames[numNestedMenus - 1].c_str() : "(none)"); if (line != 0) { lcd.print("\nLine "); lcd.print(line); + if (errorColumn != 0) + { + lcd.print(" column "); + lcd.print(errorColumn); + } } lcd.write('\n'); lcd.print(msg); - if (numNestedMenus > 1) - { - // TODO add control to pop previous menu here, or revert to main menu after some time - } + lastActionTime = millis(); + timeoutValue = ErrorTimeout; } // Parse a line in a menu layout file returning any error message, or nullptr if there was no error. // Leading whitespace has already been skipped. -const char *Menu::ParseMenuLine(char *commandWord) +const char *Menu::ParseMenuLine(char * const commandWord) { + errorColumn = 0; + // Check for blank or comment line if (*commandWord == ';' || *commandWord == 0) { @@ -213,12 +213,13 @@ const char *Menu::ParseMenuLine(char *commandWord) } if (args == commandWord || (*args != ' ' && *args != '\t' && *args != 0)) { + errorColumn = (args - commandWord) + 1; return "Bad command"; } if (*args != 0) { - *args = 0; // null terminate command word + *args = 0; // null terminate the command word ++args; } @@ -275,6 +276,7 @@ const char *Menu::ParseMenuLine(char *commandWord) case 'I': if (*args != '"') { + errorColumn = (args - commandWord) + 1; return "Missing string arg"; } ++args; @@ -291,6 +293,7 @@ const char *Menu::ParseMenuLine(char *commandWord) break; default: + errorColumn = (args - commandWord); return "Bad arg letter"; } } @@ -349,12 +352,13 @@ const char *Menu::ParseMenuLine(char *commandWord) const char * const actionString = AppendString(action); const char *const dir = AppendString(dirpath); const char *const acFileString = AppendString(fname); - AddItem(new FilesMenuItem(row, column, fontNumber, actionString, dir, acFileString, nparam, fonts[fontNumber]->height), true); - //TODO update row by a sensible value e.g. nparam * text row height + AddItem(new FilesMenuItem(row, 0, fontNumber, actionString, dir, acFileString, nparam, fonts[fontNumber]->height), true); + row += nparam * fonts[fontNumber]->height; column = 0; } else { + errorColumn = 1; return "Unknown command"; } @@ -366,13 +370,13 @@ void Menu::ResetCache() // Delete the existing items while (selectableItems != nullptr) { - MenuItem *current = selectableItems; + MenuItem * const current = selectableItems; selectableItems = selectableItems->GetNext(); delete current; } while (unSelectableItems != nullptr) { - MenuItem *current = unSelectableItems; + MenuItem * const current = unSelectableItems; unSelectableItems = unSelectableItems->GetNext(); delete current; } @@ -587,8 +591,8 @@ void Menu::EncoderAction(int action) EncoderAction_EnterItemHelper(); } - timeoutEnabled = true; lastActionTime = millis(); + timeoutValue = InactivityTimeout; } /*static*/ const char *Menu::SkipWhitespace(const char *s) { @@ -611,7 +615,7 @@ void Menu::EncoderAction(int action) void Menu::LoadImage(const char *fname) { //TODO - lcd.print("<image>"); + lcd.print("[img]"); } // Refresh is called every Spin() of the Display under most circumstances; an appropriate place to check if timeout action needs to be taken @@ -619,23 +623,20 @@ void Menu::Refresh() { if (!reprap.GetPlatform().GetMassStorage()->IsDriveMounted(0)) { - if (!bInFixedMenu()) + if (!displayingFixedMenu) { // When the SD card is not mounted, we show a fixed menu for graceful recovery - numNestedMenus = 0; LoadFixedMenu(); } } - else if (bInFixedMenu() || (timeoutEnabled && (millis() - lastActionTime > 20000))) + else if (displayingFixedMenu || (timeoutValue != 0 && (millis() - lastActionTime > timeoutValue))) { - // Showing fixed menu but SD card is now mounted, or 20 seconds following latest user action - + // Showing fixed menu but SD card is now mounted, or 10 seconds following latest user action // Go to the top menu (just discard information) numNestedMenus = 0; Load("main"); - timeoutEnabled = false; - // m_uLastActionTime = millis(); + timeoutValue = 0; } const PixelNumber rightMargin = NumCols - currentMargin; diff --git a/src/Display/Menu.h b/src/Display/Menu.h index e6658c64..925338f1 100644 --- a/src/Display/Menu.h +++ b/src/Display/Menu.h @@ -23,10 +23,9 @@ public: private: void LoadFixedMenu(); - bool bInFixedMenu() const; void ResetCache(); void Reload(); - const char *ParseMenuLine(char *s); + const char *ParseMenuLine(char * s); void LoadError(const char *msg, unsigned int line); void AddItem(MenuItem *item, bool isSelectable); const char *AppendString(const char *s); @@ -54,7 +53,7 @@ private: const LcdFont * const *fonts; const size_t numFonts; - bool timeoutEnabled; + uint32_t timeoutValue; // how long to time out after 0 = no timeout uint32_t lastActionTime; MenuItem *selectableItems; // selectable items at the innermost level @@ -64,11 +63,11 @@ private: int numSelectableItems; int highlightedItem; bool itemIsSelected; - - static constexpr const char *const m_pcFixedMenu = "zzFixed"; + bool displayingFixedMenu; // Variables used while parsing size_t commandBufferIndex; + unsigned int errorColumn; // column in the current line at which ParseMenuLine hit an error MenuItem::FontNumber fontNumber; PixelNumber currentMargin; PixelNumber row, column; diff --git a/src/GCodes/DriverMode.h b/src/GCodes/DriverMode.h deleted file mode 100644 index 594fbcf3..00000000 --- a/src/GCodes/DriverMode.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * DriverModes.h - * - * Created on: 27 Apr 2018 - * Author: David - */ - -#ifndef SRC_GCODES_DRIVERMODE_H_ -#define SRC_GCODES_DRIVERMODE_H_ - -enum class DriverMode : unsigned int -{ - constantOffTime = 0, - randomOffTime, - spreadCycle, - stealthChop, // includes stealthChop2 - unknown // must be last! -}; - -const char* TranslateDriverMode(unsigned int mode); - -inline const char* TranslateDriverMode(DriverMode mode) -{ - return TranslateDriverMode((unsigned int)mode); -} - -#endif /* SRC_GCODES_DRIVERMODE_H_ */ diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 88c1c9fb..aca91dfe 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -59,6 +59,33 @@ void GCodes::RawMove::SetDefaults() yAxes = DefaultYAxisMapping; } +#ifdef SUPPORT_OBJECT_MODEL + +// Object model table and functions +// Note: if using GCC version 7.3.1 20180622 then if lambda functions are used in this table, you must compile this file with option -std=gnu++17. +// Otherwise the table will be allocate in RAM instead of flash, which wastes too much RAM. + +// Macro to build a standard lambda function that includes the necessary type conversions +#define OBJECT_MODEL_FUNC(_ret) [] (ObjectModel* arg) { GCodes * const self = static_cast<GCodes*>(arg); return (void *)(_ret); } + +const ObjectModelTableEntry GCodes::objectModelTable[] = +{ + { "speedFactor", OBJECT_MODEL_FUNC(&(self->speedFactor)), TYPE_OF(float), ObjectModelTableEntry::none } +}; + +const char *GCodes::GetModuleName() const +{ + return nullptr; // this module has no name and doesn't need one +} + +const ObjectModelTableEntry *GCodes::GetObjectModelTable(size_t& numEntries) const +{ + numEntries = ARRAY_SIZE(objectModelTable); + return objectModelTable; +} + +#endif + GCodes::GCodes(Platform& p) : platform(p), machineType(MachineType::fff), active(false), #if HAS_VOLTAGE_MONITOR diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index 7afa207f..1e7da7e4 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -105,6 +105,9 @@ enum class StopPrintReason // The GCode interpreter class GCodes +#ifdef SUPPORT_OBJECT_MODEL + : public ObjectModel +#endif { public: struct RawMove @@ -226,6 +229,14 @@ public: void HandleReply(GCodeBuffer& gb, GCodeResult rslt, const char *reply); // Handle G-Code replies void EmergencyStop(); // Cancel everything +#ifdef SUPPORT_OBJECT_MODEL +protected: + const char *GetModuleName() const override; + const ObjectModelTableEntry *GetObjectModelTable(size_t& numEntries) const override; + + static const ObjectModelTableEntry objectModelTable[]; +#endif + private: GCodes(const GCodes&); // private copy constructor to prevent copying diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index 81a5599c..9e75e2d9 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -20,6 +20,7 @@ #include "Tools/Tool.h" #include "FilamentMonitors/FilamentMonitor.h" #include "Libraries/General/IP4String.h" +#include "Movement/StepperDrivers/DriverMode.h" #include "Version.h" #if SUPPORT_IOBITS @@ -3197,7 +3198,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) if (gb.Seen('C')) // set chopper control register { seen = true; - if (!SmartDrivers::SetChopperControlRegister(drive, gb.GetUIValue())) + if (!SmartDrivers::SetRegister(drive, SmartDriverRegister::chopperControl, gb.GetUIValue())) { reply.printf("Bad ccr for driver %u", drive); result = GCodeResult::error; @@ -3208,7 +3209,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) if (gb.Seen('F')) { seen = true; - if (!SmartDrivers::SetOffTime(drive, gb.GetUIValue())) + if (!SmartDrivers::SetRegister(drive, SmartDriverRegister::toff, gb.GetUIValue())) { reply.printf("Bad off time for driver %u", drive); result = GCodeResult::error; @@ -3229,7 +3230,8 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) if (drive < platform.GetNumSmartDrivers()) { reply.catf(", mode %s, ccr 0x%05" PRIx32 ", off time %" PRIu32, - TranslateDriverMode(SmartDrivers::GetDriverMode(drive)), SmartDrivers::GetChopperControlRegister(drive), SmartDrivers::GetOffTime(drive)); + TranslateDriverMode(SmartDrivers::GetDriverMode(drive)), + SmartDrivers::GetRegister(drive, SmartDriverRegister::chopperControl), SmartDrivers::GetRegister(drive, SmartDriverRegister::toff)); } #endif } diff --git a/src/Libraries/General/SafeVsnprintf.cpp b/src/Libraries/General/SafeVsnprintf.cpp index 9b171ce0..8e102cf0 100644 --- a/src/Libraries/General/SafeVsnprintf.cpp +++ b/src/Libraries/General/SafeVsnprintf.cpp @@ -527,7 +527,7 @@ static void tiny_print(SStringBuf& apBuf, const char *format, va_list args) { const char *s = va_arg(args, const char *); apBuf.flags.isString = true; - if (!prints(apBuf, (s != nullptr) ? s : "(null)")) + if (!prints(apBuf, (s != nullptr) ? s : "<null>")) { break; } diff --git a/src/GCodes/DriverMode.cpp b/src/Movement/StepperDrivers/DriverMode.cpp index 6450e115..6450e115 100644 --- a/src/GCodes/DriverMode.cpp +++ b/src/Movement/StepperDrivers/DriverMode.cpp diff --git a/src/Movement/StepperDrivers/DriverMode.h b/src/Movement/StepperDrivers/DriverMode.h new file mode 100644 index 00000000..4114120b --- /dev/null +++ b/src/Movement/StepperDrivers/DriverMode.h @@ -0,0 +1,42 @@ +/* + * DriverModes.h + * + * Created on: 27 Apr 2018 + * Author: David + */ + +#ifndef SRC_MOVEMENT_STEPPERDRIVERS_DRIVERMODE_H_ +#define SRC_MOVEMENT_STEPPERDRIVERS_DRIVERMODE_H_ + +enum class DriverMode : unsigned int +{ + constantOffTime = 0, + randomOffTime, + spreadCycle, + stealthChop, // includes stealthChop2 + unknown // must be last! +}; + +const char* TranslateDriverMode(unsigned int mode); + +inline const char* TranslateDriverMode(DriverMode mode) +{ + return TranslateDriverMode((unsigned int)mode); +} + +// Register codes used to implement M569 command parameters. +// This common set is used for all smart drivers. Not all are complete registers, some are just parts of registers. + +enum class SmartDriverRegister : unsigned int +{ + toff, + tblank, + hstart, + hend, + hdec, + chopperControl, + coolStep, + tpwmthrs +}; + +#endif /* SRC_MOVEMENT_STEPPERDRIVERS_DRIVERMODE_H_ */ diff --git a/src/Movement/StepperDrivers/TMC22xx.cpp b/src/Movement/StepperDrivers/TMC22xx.cpp index 0515be81..8f5570e1 100644 --- a/src/Movement/StepperDrivers/TMC22xx.cpp +++ b/src/Movement/StepperDrivers/TMC22xx.cpp @@ -140,7 +140,10 @@ constexpr uint32_t DefaultIholdIrunReg = (0 << IHOLDIRUN_IHOLD_SHIFT) | (0 << IH constexpr uint8_t REGNUM_TPOWER_DOWN = 0x11; constexpr uint8_t REGNUM_TSTEP = 0x12; + constexpr uint8_t REGNUM_TPWMTHRS = 0x13; +constexpr uint32_t DefaultTpwmthrsReg = 1000; //TODO find a sensible default + constexpr uint8_t REGNUM_VACTUAL = 0x22; // Sequencer registers (read only) @@ -239,11 +242,6 @@ public: void Init(uint32_t p_driverNumber, Pin p_pin); void SetAxisNumber(size_t p_axisNumber); void WriteAll(); - bool SetChopConf(uint32_t newVal); - bool SetOffTime(uint32_t newVal); - uint32_t GetChopConf() const; - uint32_t GetOffTime() const; - void SetCoolStep(uint16_t coolStepConfig); bool SetMicrostepping(uint32_t shift, bool interpolate); unsigned int GetMicrostepping(bool& interpolation) const; // Get microstepping bool SetDriverMode(unsigned int mode); @@ -255,6 +253,9 @@ public: bool UpdatePending() const { return registersToUpdate != 0; } bool UsesGlobalEnable() const { return enablePin == NoPin; } + bool SetRegister(SmartDriverRegister reg, uint32_t regVal); + uint32_t GetRegister(SmartDriverRegister reg) const; + float GetStandstillCurrentPercent() const; void SetStandstillCurrentPercent(float percent); @@ -273,6 +274,7 @@ public: void UartTmcHandler(); // core of the ISR for this driver private: + bool SetChopConf(uint32_t newVal); void UpdateRegister(size_t regIndex, uint32_t regVal); void UpdateChopConfRegister(); // calculate the chopper control register and flag it for sending void UpdateCurrent(); @@ -286,7 +288,7 @@ private: void SetupDMAReceive(uint8_t regnum, uint8_t crc) __attribute__ ((hot)); // set up the PDC to receive a register #endif - static constexpr unsigned int NumWriteRegisters = 5; // the number of registers that we write to + static constexpr unsigned int NumWriteRegisters = 6; // the number of registers that we write to static const uint8_t WriteRegNumbers[NumWriteRegisters]; // the register numbers that we write to // Write register numbers are in priority order, most urgent first, in same order as WriteRegNumbers @@ -295,6 +297,7 @@ private: static constexpr unsigned int WriteChopConf = 2; // enable/disable and microstep setting static constexpr unsigned int WriteIholdIrun = 3; // current setting static constexpr unsigned int WritePwmConf = 4; // read register select, sense voltage high/low sensitivity + static constexpr unsigned int WriteTpwmthrs = 5; // upper step rate limit for stealthchop static constexpr unsigned int NumReadRegisters = 2; // the number of registers that we read from static const uint8_t ReadRegNumbers[NumReadRegisters]; // the register numbers that we read from @@ -374,7 +377,8 @@ const uint8_t TmcDriverState::WriteRegNumbers[NumWriteRegisters] = REGNUM_SLAVECONF, REGNUM_CHOPCONF, REGNUM_IHOLDIRUN, - REGNUM_PWMCONF + REGNUM_PWMCONF, + REGNUM_TPWMTHRS }; const uint8_t TmcDriverState::ReadRegNumbers[NumReadRegisters] = @@ -484,6 +488,7 @@ pre(!driversPowered) SetMicrostepping(DefaultMicrosteppingShift, DefaultInterpolation); // this also updates the chopper control register UpdateRegister(WriteIholdIrun, DefaultIholdIrunReg); UpdateRegister(WritePwmConf, DefaultPwmConfReg); + UpdateRegister(WriteTpwmthrs, DefaultTpwmthrsReg); for (size_t i = 0; i < NumReadRegisters; ++i) { accumulatedReadRegisters[i] = readRegisters[i] = 0; @@ -536,40 +541,83 @@ unsigned int TmcDriverState::GetMicrostepping(bool& interpolation) const return 1u << microstepShiftFactor; } -// Set the chopper control register to the settings provided by the user. We allow only the lowest 17 bits to be set. -bool TmcDriverState::SetChopConf(uint32_t newVal) +bool TmcDriverState::SetRegister(SmartDriverRegister reg, uint32_t regVal) { - const uint32_t offTime = (newVal & CHOPCONF_TOFF_MASK) >> CHOPCONF_TOFF_SHIFT; - if (offTime == 0 || (offTime == 1 && (configuredChopConfReg & CHOPCONF_TBL_MASK) < (2 << CHOPCONF_TBL_SHIFT))) + switch(reg) { + case SmartDriverRegister::chopperControl: + return SetChopConf(regVal); + + case SmartDriverRegister::toff: + return SetChopConf((configuredChopConfReg & ~CHOPCONF_TOFF_MASK) | ((regVal << CHOPCONF_TOFF_SHIFT) & CHOPCONF_TOFF_MASK)); + + case SmartDriverRegister::tblank: + return SetChopConf((configuredChopConfReg & ~CHOPCONF_TBL_MASK) | ((regVal << CHOPCONF_TBL_SHIFT) & CHOPCONF_TBL_MASK)); + + case SmartDriverRegister::hstart: + return SetChopConf((configuredChopConfReg & ~CHOPCONF_HSTRT_MASK) | ((regVal << CHOPCONF_HSTRT_SHIFT) & CHOPCONF_HSTRT_MASK)); + + case SmartDriverRegister::hend: + return SetChopConf((configuredChopConfReg & ~CHOPCONF_HEND_MASK) | ((regVal << CHOPCONF_HEND_SHIFT) & CHOPCONF_HEND_MASK)); + + case SmartDriverRegister::tpwmthrs: + UpdateRegister(WriteTpwmthrs, regVal & ((1u << 20) - 1)); + return true; + + case SmartDriverRegister::hdec: + case SmartDriverRegister::coolStep: + default: return false; } - const uint32_t userMask = CHOPCONF_TBL_MASK | CHOPCONF_HSTRT_MASK | CHOPCONF_HEND_MASK | CHOPCONF_TOFF_MASK; // mask of bits the user is allowed to change - configuredChopConfReg = (configuredChopConfReg & ~userMask) | (newVal & userMask); - UpdateChopConfRegister(); - return true; } -// Set the off time in the chopper control register -bool TmcDriverState::SetOffTime(uint32_t newVal) +uint32_t TmcDriverState::GetRegister(SmartDriverRegister reg) const { - if (newVal > 15) + switch(reg) { - return false; - } - return SetChopConf((configuredChopConfReg & ~CHOPCONF_TOFF_MASK) | ((newVal << CHOPCONF_TOFF_SHIFT) & CHOPCONF_TOFF_MASK)); -} + case SmartDriverRegister::chopperControl: + return configuredChopConfReg & 0x01FFFF; -// Get microstepping or chopper control register -uint32_t TmcDriverState::GetChopConf() const -{ - return configuredChopConfReg & 0x01FFFF; + case SmartDriverRegister::toff: + return (configuredChopConfReg & CHOPCONF_TOFF_MASK) >> CHOPCONF_TOFF_SHIFT; + + case SmartDriverRegister::tblank: + return (configuredChopConfReg & CHOPCONF_TBL_MASK) >> CHOPCONF_TBL_SHIFT; + + case SmartDriverRegister::hstart: + return (configuredChopConfReg & CHOPCONF_HSTRT_MASK) >> CHOPCONF_HSTRT_SHIFT; + + case SmartDriverRegister::hend: + return (configuredChopConfReg & CHOPCONF_HEND_MASK) >> CHOPCONF_HEND_SHIFT; + + case SmartDriverRegister::tpwmthrs: + return writeRegisters[WriteTpwmthrs]; + + case SmartDriverRegister::hdec: + case SmartDriverRegister::coolStep: + default: + return 0; + } } -// Get the off time from the chopper control register -uint32_t TmcDriverState::GetOffTime() const +// Set the chopper control register to the settings provided by the user. We allow only the lowest 17 bits to be set. +bool TmcDriverState::SetChopConf(uint32_t newVal) { - return (configuredChopConfReg & CHOPCONF_TOFF_MASK) >> CHOPCONF_TOFF_SHIFT; + const uint32_t offTime = (newVal & CHOPCONF_TOFF_MASK) >> CHOPCONF_TOFF_SHIFT; + if (offTime == 0 || (offTime == 1 && (newVal & CHOPCONF_TBL_MASK) < (2 << CHOPCONF_TBL_SHIFT))) + { + return false; + } + const uint32_t hstrt = (newVal & CHOPCONF_HSTRT_MASK) >> CHOPCONF_HSTRT_SHIFT; + const uint32_t hend = (newVal & CHOPCONF_HEND_MASK) >> CHOPCONF_HEND_SHIFT; + if (hstrt + hend > 16) + { + return false; + } + const uint32_t userMask = CHOPCONF_TBL_MASK | CHOPCONF_HSTRT_MASK | CHOPCONF_HEND_MASK | CHOPCONF_TOFF_MASK; // mask of bits the user is allowed to change + configuredChopConfReg = (configuredChopConfReg & ~userMask) | (newVal & userMask); + UpdateChopConfRegister(); + return true; } // Set the driver mode @@ -1002,26 +1050,6 @@ namespace SmartDrivers return (driver < numTmc22xxDrivers) ? driverStates[driver].GetDriverMode() : DriverMode::unknown; } - bool SetChopperControlRegister(size_t driver, uint32_t ccr) - { - return driver < numTmc22xxDrivers && driverStates[driver].SetChopConf(ccr); - } - - uint32_t GetChopperControlRegister(size_t driver) - { - return (driver < numTmc22xxDrivers) ? driverStates[driver].GetChopConf() : 0; - } - - bool SetOffTime(size_t driver, uint32_t offTime) - { - return driver < numTmc22xxDrivers && driverStates[driver].SetOffTime(offTime); - } - - uint32_t GetOffTime(size_t driver) - { - return (driver < numTmc22xxDrivers) ? driverStates[driver].GetOffTime() : 0; - } - // Flag that the the drivers have been powered up or down and handle any timeouts // Before the first call to this function with 'powered' true, you must call Init() void Spin(bool powered) @@ -1107,11 +1135,6 @@ namespace SmartDrivers // We don't use it with TMC22xx drivers. } - void SetCoolStep(size_t drive, uint16_t coolStepConfig) - { - // Not supported on the TMC22xx - } - void AppendDriverStatus(size_t drive, const StringRef& reply) { if (drive < numTmc22xxDrivers) @@ -1131,7 +1154,16 @@ namespace SmartDrivers { driverStates[drive].SetStandstillCurrentPercent(percent); } + } + bool SetRegister(size_t driver, SmartDriverRegister reg, uint32_t regVal) + { + return (driver < numTmc22xxDrivers) && driverStates[driver].SetRegister(reg, regVal); + } + + uint32_t GetRegister(size_t driver, SmartDriverRegister reg) + { + return (driver < numTmc22xxDrivers) ? driverStates[driver].GetRegister(reg) : 0; } }; // end namespace diff --git a/src/Movement/StepperDrivers/TMC22xx.h b/src/Movement/StepperDrivers/TMC22xx.h index 7aed6ca2..38e3261d 100644 --- a/src/Movement/StepperDrivers/TMC22xx.h +++ b/src/Movement/StepperDrivers/TMC22xx.h @@ -17,7 +17,7 @@ #endif #include "RepRapFirmware.h" -#include "GCodes/DriverMode.h" +#include "DriverMode.h" #include "Pins.h" #include "MessageType.h" #include "Libraries/General/StringRef.h" @@ -48,16 +48,13 @@ namespace SmartDrivers unsigned int GetMicrostepping(size_t drive, bool& interpolation); bool SetDriverMode(size_t driver, unsigned int mode); DriverMode GetDriverMode(size_t driver); - bool SetChopperControlRegister(size_t driver, uint32_t ccr); - uint32_t GetChopperControlRegister(size_t driver); - bool SetOffTime(size_t driver, uint32_t ccr); - uint32_t GetOffTime(size_t driver); void Spin(bool powered); void TurnDriversOff(); - void SetCoolStep(size_t drive, uint16_t coolStepConfig); void AppendDriverStatus(size_t drive, const StringRef& reply); float GetStandstillCurrentPercent(size_t drive); void SetStandstillCurrentPercent(size_t drive, float percent); + bool SetRegister(size_t driver, SmartDriverRegister reg, uint32_t regVal); + uint32_t GetRegister(size_t driver, SmartDriverRegister reg); }; #endif diff --git a/src/Movement/StepperDrivers/TMC2660.cpp b/src/Movement/StepperDrivers/TMC2660.cpp index 4c439f9e..62737fae 100644 --- a/src/Movement/StepperDrivers/TMC2660.cpp +++ b/src/Movement/StepperDrivers/TMC2660.cpp @@ -77,14 +77,20 @@ const uint32_t TMC_DRVCONF_TST = 1 << 16; // Chopper control register bits const uint32_t TMC_CHOPCONF_TOFF_MASK = 15; const uint32_t TMC_CHOPCONF_TOFF_SHIFT = 0; -#define TMC_CHOPCONF_TOFF(n) ((((uint32_t)n) & 15) << 0) -#define TMC_CHOPCONF_HSTRT(n) ((((uint32_t)n) & 7) << 4) -#define TMC_CHOPCONF_HEND(n) ((((uint32_t)n) & 15) << 7) -#define TMC_CHOPCONF_HDEC(n) ((((uint32_t)n) & 3) << 11) +const uint32_t TMC_CHOPCONF_HSTRT_MASK = (7 << 4); +const uint32_t TMC_CHOPCONF_HSTRT_SHIFT = 4; +const uint32_t TMC_CHOPCONF_HEND_MASK = (15 << 7); +const uint32_t TMC_CHOPCONF_HEND_SHIFT = 7; +const uint32_t TMC_CHOPCONF_HDEC_MASK = (3 << 11); +const uint32_t TMC_CHOPCONF_HDEC_SHIFT = 11; const uint32_t TMC_CHOPCONF_RNDTF = 1 << 13; const uint32_t TMC_CHOPCONF_CHM = 1 << 14; const uint32_t TMC_CHOPCONF_TBL_MASK = (3 << 15); const uint32_t TMC_CHOPCONF_TBL_SHIFT = 15; +#define TMC_CHOPCONF_TOFF(n) ((((uint32_t)n) & 15) << 0) +#define TMC_CHOPCONF_HSTRT(n) ((((uint32_t)n) & 7) << 4) +#define TMC_CHOPCONF_HEND(n) ((((uint32_t)n) & 15) << 7) +#define TMC_CHOPCONF_HDEC(n) ((((uint32_t)n) & 3) << 11) #define TMC_CHOPCONF_TBL(n) (((uint32_t)n & 3) << 15) // Driver control register bits, when SDOFF=0 @@ -169,11 +175,6 @@ public: void SetAxisNumber(size_t p_axisNumber); void WriteAll(); - bool SetChopConf(uint32_t newVal); - bool SetOffTime(uint32_t newVal); - uint32_t GetChopConf() const; - uint32_t GetOffTime() const; - void SetCoolStep(uint16_t coolStepConfig); bool SetMicrostepping(uint32_t shift, bool interpolate); unsigned int GetMicrostepping(bool& interpolation) const; // Get microstepping bool SetDriverMode(unsigned int mode); @@ -187,6 +188,9 @@ public: void AppendStallConfig(const StringRef& reply) const; void AppendDriverStatus(const StringRef& reply); + bool SetRegister(SmartDriverRegister reg, uint32_t regVal); + uint32_t GetRegister(SmartDriverRegister reg) const; + void TransferDone() __attribute__ ((hot)); // called by the ISR when the SPI transfer has completed void StartTransfer() __attribute__ ((hot)); // called to start a transfer @@ -194,6 +198,8 @@ public: uint32_t ReadAccumulatedStatus(uint32_t bitsToKeep); private: + bool SetChopConf(uint32_t newVal); + void ResetLoadRegisters() { minSgLoadRegister = 1023; @@ -392,6 +398,70 @@ inline void TmcDriverState::WriteAll() registersToUpdate = UpdateAllRegisters; } +bool TmcDriverState::SetRegister(SmartDriverRegister reg, uint32_t regVal) +{ + switch(reg) + { + case SmartDriverRegister::chopperControl: + return SetChopConf(regVal); + + case SmartDriverRegister::coolStep: + registers[SmartEnable] = TMC_REG_SMARTEN | (regVal & 0xFFFF); + registersToUpdate |= 1u << SmartEnable; + return true; + + case SmartDriverRegister::toff: + return SetChopConf((configuredChopConfReg & ~TMC_CHOPCONF_TOFF_MASK) | ((regVal << TMC_CHOPCONF_TOFF_SHIFT) & TMC_CHOPCONF_TOFF_MASK)); + + case SmartDriverRegister::tblank: + return SetChopConf((configuredChopConfReg & ~TMC_CHOPCONF_TBL_MASK) | ((regVal << TMC_CHOPCONF_TBL_SHIFT) & TMC_CHOPCONF_TBL_MASK)); + + case SmartDriverRegister::hstart: + return SetChopConf((configuredChopConfReg & ~TMC_CHOPCONF_HSTRT_MASK) | ((regVal << TMC_CHOPCONF_HSTRT_SHIFT) & TMC_CHOPCONF_HSTRT_MASK)); + + case SmartDriverRegister::hend: + return SetChopConf((configuredChopConfReg & ~TMC_CHOPCONF_HEND_MASK) | ((regVal << TMC_CHOPCONF_HEND_SHIFT) & TMC_CHOPCONF_HEND_MASK)); + + case SmartDriverRegister::hdec: + return SetChopConf((configuredChopConfReg & ~TMC_CHOPCONF_HDEC_MASK) | ((regVal << TMC_CHOPCONF_HDEC_SHIFT) & TMC_CHOPCONF_HDEC_MASK)); + + case SmartDriverRegister::tpwmthrs: + default: + return false; + } +} + +uint32_t TmcDriverState::GetRegister(SmartDriverRegister reg) const +{ + switch(reg) + { + case SmartDriverRegister::chopperControl: + return configuredChopConfReg & TMC_DATA_MASK; + + case SmartDriverRegister::coolStep: + return registers[SmartEnable] & TMC_DATA_MASK; + + case SmartDriverRegister::toff: + return (configuredChopConfReg & TMC_CHOPCONF_TOFF_MASK) >> TMC_CHOPCONF_TOFF_SHIFT; + + case SmartDriverRegister::tblank: + return (configuredChopConfReg & TMC_CHOPCONF_TBL_MASK) >> TMC_CHOPCONF_TBL_SHIFT; + + case SmartDriverRegister::hstart: + return (configuredChopConfReg & TMC_CHOPCONF_HSTRT_MASK) >> TMC_CHOPCONF_HSTRT_SHIFT; + + case SmartDriverRegister::hend: + return (configuredChopConfReg & TMC_CHOPCONF_HEND_MASK) >> TMC_CHOPCONF_HEND_SHIFT; + + case SmartDriverRegister::hdec: + return (configuredChopConfReg & TMC_CHOPCONF_HDEC_MASK) >> TMC_CHOPCONF_HDEC_SHIFT; + + case SmartDriverRegister::tpwmthrs: + default: + return 0; + } +} + // Check the new chopper control register, update it and return true if it is legal bool TmcDriverState::SetChopConf(uint32_t newVal) { @@ -402,21 +472,20 @@ bool TmcDriverState::SetChopConf(uint32_t newVal) { return false; } + if ((newVal & TMC_CHOPCONF_CHM) == 0) + { + const uint32_t hstrt = (newVal & TMC_CHOPCONF_HSTRT_MASK) >> TMC_CHOPCONF_HSTRT_SHIFT; + const uint32_t hend = (newVal & TMC_CHOPCONF_HEND_MASK) >> TMC_CHOPCONF_HEND_SHIFT; + if (hstrt + hend > 15) + { + return false; + } + } configuredChopConfReg = (newVal & 0x0001FFFF) | TMC_REG_CHOPCONF; // save the new value UpdateChopConfRegister(); // send the new value, keeping the current Enable status return true; } -// Set the off time in the chopper control register -bool TmcDriverState::SetOffTime(uint32_t newVal) -{ - if (newVal > 15) - { - return false; - } - return SetChopConf((configuredChopConfReg & ~TMC_CHOPCONF_TOFF_MASK) | ((newVal << TMC_CHOPCONF_TOFF_SHIFT) & TMC_CHOPCONF_TOFF_MASK)); -} - // Set the driver mode bool TmcDriverState::SetDriverMode(unsigned int mode) { @@ -541,12 +610,6 @@ void TmcDriverState::SetStallMinimumStepsPerSecond(unsigned int stepsPerSecond) maxStallStepInterval = StepClockRate/max<unsigned int>(stepsPerSecond, 1); } -void TmcDriverState::SetCoolStep(uint16_t coolStepConfig) -{ - registers[SmartEnable] = TMC_REG_SMARTEN | coolStepConfig; - registersToUpdate |= 1u << SmartEnable; -} - void TmcDriverState::AppendStallConfig(const StringRef& reply) const { const bool filtered = ((registers[StallGuardConfig] & TMC_SGCSCONF_SGT_SFILT) != 0); @@ -609,18 +672,6 @@ unsigned int TmcDriverState::GetMicrostepping(bool& interpolation) const return 1u << microstepShiftFactor; } -// Get chopper control register -uint32_t TmcDriverState::GetChopConf() const -{ - return configuredChopConfReg & TMC_DATA_MASK; -} - -// Get the off time from the chopper control register -uint32_t TmcDriverState::GetOffTime() const -{ - return (configuredChopConfReg & TMC_CHOPCONF_TOFF_MASK) >> TMC_CHOPCONF_TOFF_SHIFT; -} - // This is called by the ISR when the SPI transfer has completed inline void TmcDriverState::TransferDone() { @@ -896,26 +947,6 @@ namespace SmartDrivers return (driver < numTmc2660Drivers) ? driverStates[driver].GetDriverMode() : DriverMode::unknown; } - bool SetChopperControlRegister(size_t driver, uint32_t ccr) - { - return driver < numTmc2660Drivers && driverStates[driver].SetChopConf(ccr); - } - - uint32_t GetChopperControlRegister(size_t driver) - { - return (driver < numTmc2660Drivers) ? driverStates[driver].GetChopConf() : 0; - } - - bool SetOffTime(size_t driver, uint32_t offTime) - { - return driver < numTmc2660Drivers && driverStates[driver].SetOffTime(offTime); - } - - uint32_t GetOffTime(size_t driver) - { - return (driver < numTmc2660Drivers) ? driverStates[driver].GetOffTime() : 0; - } - // Flag the the drivers have been powered up. // Before the first call to this function with powered true, you must call Init(). void Spin(bool powered) @@ -979,14 +1010,6 @@ namespace SmartDrivers } } - void SetCoolStep(size_t drive, uint16_t coolStepConfig) - { - if (drive < numTmc2660Drivers) - { - driverStates[drive].SetCoolStep(coolStepConfig); - } - } - void AppendStallConfig(size_t driver, const StringRef& reply) { if (driver < numTmc2660Drivers) @@ -1013,6 +1036,16 @@ namespace SmartDrivers // not supported so nothing to see here } + bool SetRegister(size_t driver, SmartDriverRegister reg, uint32_t regVal) + { + return (driver < numTmc2660Drivers) && driverStates[driver].SetRegister(reg, regVal); + } + + uint32_t GetRegister(size_t driver, SmartDriverRegister reg) + { + return (driver < numTmc2660Drivers) ? driverStates[driver].GetRegister(reg) : 0; + } + }; // end namespace #endif diff --git a/src/Movement/StepperDrivers/TMC2660.h b/src/Movement/StepperDrivers/TMC2660.h index a94aedcb..e10795a4 100644 --- a/src/Movement/StepperDrivers/TMC2660.h +++ b/src/Movement/StepperDrivers/TMC2660.h @@ -11,7 +11,7 @@ #if SUPPORT_TMC2660 #include "RepRapFirmware.h" -#include "GCodes/DriverMode.h" +#include "DriverMode.h" #include "Pins.h" #include "MessageType.h" #include "Libraries/General/StringRef.h" @@ -42,18 +42,15 @@ namespace SmartDrivers unsigned int GetMicrostepping(size_t drive, bool& interpolation); bool SetDriverMode(size_t driver, unsigned int mode); DriverMode GetDriverMode(size_t driver); - bool SetChopperControlRegister(size_t driver, uint32_t ccr); - uint32_t GetChopperControlRegister(size_t driver); - bool SetOffTime(size_t driver, uint32_t ccr); - uint32_t GetOffTime(size_t driver); void SetStallThreshold(size_t driver, int sgThreshold); void SetStallFilter(size_t driver, bool sgFilter); void SetStallMinimumStepsPerSecond(size_t driver, unsigned int stepsPerSecond); - void SetCoolStep(size_t driver, uint16_t coolStepConfig); void AppendStallConfig(size_t driver, const StringRef& reply); void AppendDriverStatus(size_t driver, const StringRef& reply); float GetStandstillCurrentPercent(size_t driver); void SetStandstillCurrentPercent(size_t driver, float percent); + bool SetRegister(size_t driver, SmartDriverRegister reg, uint32_t regVal); + uint32_t GetRegister(size_t driver, SmartDriverRegister reg); }; #endif diff --git a/src/Movement/StepperDrivers/TMC51xx.cpp b/src/Movement/StepperDrivers/TMC51xx.cpp index 2c5efd18..07c88bc0 100644 --- a/src/Movement/StepperDrivers/TMC51xx.cpp +++ b/src/Movement/StepperDrivers/TMC51xx.cpp @@ -196,11 +196,6 @@ public: void Init(uint32_t p_driverNumber, Pin p_pin); void SetAxisNumber(size_t p_axisNumber); void WriteAll(); - bool SetChopConf(uint32_t newVal); - bool SetOffTime(uint32_t newVal); - uint32_t GetChopConf() const; - uint32_t GetOffTime() const; - void SetCoolStep(uint16_t coolStepConfig); bool SetMicrostepping(uint32_t shift, bool interpolate); unsigned int GetMicrostepping(bool& interpolation) const; // Get microstepping bool SetDriverMode(unsigned int mode); @@ -215,6 +210,9 @@ public: void SetStallMinimumStepsPerSecond(unsigned int stepsPerSecond); void AppendStallConfig(const StringRef& reply) const; + bool SetRegister(SmartDriverRegister reg, uint32_t regVal); + uint32_t GetRegister(SmartDriverRegister reg) const; + float GetStandstillCurrentPercent() const; void SetStandstillCurrentPercent(float percent); @@ -246,7 +244,7 @@ private: void SetupDMAReceive(uint8_t regnum, uint8_t crc) __attribute__ ((hot)); // set up the PDC to receive a register #endif - static constexpr unsigned int NumWriteRegisters = 5; // the number of registers that we write to + static constexpr unsigned int NumWriteRegisters = 6; // the number of registers that we write to static const uint8_t WriteRegNumbers[NumWriteRegisters]; // the register numbers that we write to // Write register numbers are in priority order, most urgent first, in same order as WriteRegNumbers @@ -255,6 +253,7 @@ private: static constexpr unsigned int WriteChopConf = 2; // enable/disable and microstep setting static constexpr unsigned int WriteIholdIrun = 3; // current setting static constexpr unsigned int WritePwmConf = 4; // read register select, sense voltage high/low sensitivity + static constexpr unsigned int WriteTpwmthrs = 5; // upper step rate limit for stealthchop static constexpr unsigned int NumReadRegisters = 2; // the number of registers that we read from static const uint8_t ReadRegNumbers[NumReadRegisters]; // the register numbers that we read from @@ -447,26 +446,6 @@ namespace SmartDrivers return (driver < numTmc51xxDrivers) ? driverStates[driver].GetDriverMode() : DriverMode::unknown; } - bool SetChopperControlRegister(size_t driver, uint32_t ccr) - { - return driver < numTmc51xxDrivers && driverStates[driver].SetChopConf(ccr); - } - - uint32_t GetChopperControlRegister(size_t driver) - { - return (driver < numTmc51xxDrivers) ? driverStates[driver].GetChopConf() : 0; - } - - bool SetOffTime(size_t driver, uint32_t offTime) - { - return driver < numTmc51xxDrivers && driverStates[driver].SetOffTime(offTime); - } - - uint32_t GetOffTime(size_t driver) - { - return (driver < numTmc51xxDrivers) ? driverStates[driver].GetOffTime() : 0; - } - // Flag that the the drivers have been powered up or down and handle any timeouts // Before the first call to this function with 'powered' true, you must call Init() void Spin(bool powered) @@ -577,14 +556,6 @@ namespace SmartDrivers } } - void SetCoolStep(size_t drive, uint16_t coolStepConfig) - { - if (drive < numTmc51xxDrivers) - { - driverStates[drive].SetCoolStep(coolStepConfig); - } - } - void AppendStallConfig(size_t driver, const StringRef& reply) { if (driver < numTmc51xxDrivers) @@ -611,7 +582,18 @@ namespace SmartDrivers // not supported so nothing to see here } + bool SetRegister(size_t driver, SmartDriverRegister reg, uint32_t regVal) + { + return (driver < numTmc22xxDrivers) && driverStates[driver].SetRegister(reg, regVal); + } + + uint32_t GetRegister(size_t driver, SmartDriverRegister reg) + { + return (driver < numTmc22xxDrivers) ? driverStates[driver].GetRegister(reg) : 0; + } + }; // end namespace + #endif // End diff --git a/src/Movement/StepperDrivers/TMC51xx.h b/src/Movement/StepperDrivers/TMC51xx.h index 587a73cf..a9c215ff 100644 --- a/src/Movement/StepperDrivers/TMC51xx.h +++ b/src/Movement/StepperDrivers/TMC51xx.h @@ -41,18 +41,15 @@ namespace SmartDrivers unsigned int GetMicrostepping(size_t drive, bool& interpolation); bool SetDriverMode(size_t driver, unsigned int mode); DriverMode GetDriverMode(size_t driver); - bool SetChopperControlRegister(size_t driver, uint32_t ccr); - uint32_t GetChopperControlRegister(size_t driver); - bool SetOffTime(size_t driver, uint32_t ccr); - uint32_t GetOffTime(size_t driver); void SetStallThreshold(size_t driver, int sgThreshold); void SetStallFilter(size_t driver, bool sgFilter); void SetStallMinimumStepsPerSecond(size_t driver, unsigned int stepsPerSecond); - void SetCoolStep(size_t driver, uint16_t coolStepConfig); void AppendStallConfig(size_t driver, const StringRef& reply); void AppendDriverStatus(size_t driver, const StringRef& reply); float GetStandstillCurrentPercent(size_t driver); void SetStandstillCurrentPercent(size_t driver, float percent); + bool SetRegister(size_t driver, SmartDriverRegister reg, uint32_t regVal); + uint32_t GetRegister(size_t driver, SmartDriverRegister reg); }; #endif diff --git a/src/NamedEnum.h b/src/NamedEnum.h new file mode 100644 index 00000000..0f322926 --- /dev/null +++ b/src/NamedEnum.h @@ -0,0 +1,53 @@ +/* + * NamedEnum.h + * + * Created on: 27 Aug 2018 + * Author: David + */ + +#ifndef SRC_NAMEDENUM_H_ +#define SRC_NAMEDENUM_H_ + +// Plumbing to allow overloaded STRINGLIST macro +#define CAT( A, B ) A ## B +#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) +#define GET_COUNT( _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, COUNT, ... ) COUNT +#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ) +#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__) + +// Macro to turn a list of names into a list of strings +#define STRINGLIST( ... ) VA_SELECT( STRINGLIST, __VA_ARGS__ ) +#define STRINGLIST_2(_v1,_v2) #_v1,#_v2 +#define STRINGLIST_3(_v1,_v2,_v3) #_v1,#_v2,#_v3 +#define STRINGLIST_4(_v1,_v2,_v3,_v4) #_v1,#_v2,#_v3,#_v4 +#define STRINGLIST_5(_v1,_v2,_v3,_v4,_v5) #_v1,#_v2,#_v3,#_v4,_v5 + +// Macro to declare an enumeration with printable value names +// Usage example: +// NamedEnum(MakeOfCar, ford, vauxhall, bmw); +// MakeOfCar mycar(MakeOfCar::bmw); +// myCar = MakeOfCar::ford; +// if (myCar == MakeOfCar::vauxhall) { ... } +// printf("%s", myCar.ToString()); +#define NamedEnum(_typename, _v1, ...) \ +class _typename { \ +public: \ + enum _E : unsigned int { _v1 = 0, __VA_ARGS__ }; /* underlying enumeration */ \ + static constexpr unsigned int NumValues = VA_SIZE(__VA_ARGS__) + 1; /* count of members */ \ + explicit _typename(_E arg) { v = arg; } /* constructor */ \ + _typename(const _typename& arg) { v = arg.v; } /* copy constructor */ \ + bool operator==(_typename arg) const { return v == arg.v; } /* equality operator */ \ + bool operator!=(_typename arg) const { return v != arg.v; } /* inequality operator */ \ + bool operator==(_E arg) const { return v == arg; } /* equality operator */ \ + bool operator!=(_E arg) const { return v != arg; } /* inequality operator */ \ + const _typename& operator=(_E arg) { v = arg; return *this; } /* assignment operator from underlying enum */ \ + const _typename& operator=(_typename arg) { v = arg.v; return *this; } /* copy assignment operator */ \ + unsigned int ToInt() const { return static_cast<unsigned int>(v); } /* conversion to unsigned integer */ \ + const char* ToString() const { return (v < NumValues) ? _names[v] : "undefined"; } /* conversion to C string */ \ + void Assign(unsigned int arg) { v = static_cast<_E>(arg); } /* assignment from integer */ \ +private: \ + _E v; \ + static constexpr const char* _names[NumValues] = { STRINGLIST(_v1, __VA_ARGS__) }; \ +} + +#endif /* SRC_NAMEDENUM_H_ */ diff --git a/src/ObjectModel/ObjectModel.cpp b/src/ObjectModel/ObjectModel.cpp new file mode 100644 index 00000000..a278a219 --- /dev/null +++ b/src/ObjectModel/ObjectModel.cpp @@ -0,0 +1,308 @@ +/* + * ObjectModel.cpp + * + * Created on: 27 Aug 2018 + * Author: David + */ + +#include "ObjectModel.h" + +#ifdef SUPPORT_OBJECT_MODEL + +#include "OutputMemory.h" +#include <cstring> + +// Constructor +ObjectModel::ObjectModel() +{ +} + +// Report this object +bool ObjectModel::ReportAsJson(OutputBuffer* buf, const char* filter, ReportFlags flags) +{ + size_t numEntries; + const ObjectModelTableEntry *tbl = GetObjectModelTable(numEntries); + const char *moduleName = GetModuleName(); + if (moduleName != nullptr) // if we are not the root object + { + buf->cat(moduleName); + buf->cat(':'); + filter = GetNextElement(filter); // skip the bit that matches the name of this module + } + + buf->cat('{'); + bool added = false; + while (numEntries != 0) + { + if (added) + { + buf->cat(','); + } + if (filter[0] == 0 || tbl->Matches(filter, flags)) + { + tbl->ReportAsJson(buf, this, filter, flags); + added = true; + } + --numEntries; + ++tbl; + } + buf->cat('}'); + return true; +} + +// Find the requested entry +const ObjectModelTableEntry* ObjectModel::FindObjectModelTableEntry(const char* idString) +{ + size_t numElems; + const ObjectModelTableEntry *tbl = GetObjectModelTable(numElems); + size_t low = 0, high = numElems; + while (high > low) + { + const size_t mid = (high - low)/2 + low; + const int t = tbl[mid].IdCompare(idString); + if (t == 0) + { + return &tbl[mid]; + } + if (t > 0) + { + low = mid + 1u; + } + else + { + high = mid; + } + } + return (low < numElems && tbl[low].IdCompare(idString) == 0) ? &tbl[low] : nullptr; +} + +// Get the object model table entry for the leaf object in the query +const ObjectModelTableEntry *ObjectModel::FindObjectModelLeafEntry(const char *idString) +{ + const ObjectModelTableEntry *e = FindObjectModelTableEntry(idString); + return (e == nullptr) ? e : e->FindLeafEntry(this, idString); +} + +#if 0 // not implemented yet +bool ObjectModel::GetStringObjectValue(const StringRef& str, const char* idString) const +{ +} + +bool ObjectModel::GetLongEnumObjectValue(const StringRef& str, const char* idString) const +{ +} + +bool ObjectModel::GetShortEnumObjectValue(uint32_t& val, const char* idString) const +{ +} + +bool ObjectModel::GetBitmapObjectValue(uint32_t& val, const char* idString) const +{ +} +#endif + +#if 0 +bool ObjectModel::SetFloatObjectValue(float val, const char* idString) +{ +} + +bool ObjectModel::SetUnsignedObjectValue(uint32_t val, const char* idString) +{ +} + +bool ObjectModel::SetSignedObjectValue(int32_t val, const char* idString) +{ +} + +bool ObjectModel::SetStringObjectValue(const StringRef& str, const char* idString) +{ +} + +bool ObjectModel::SetLongEnumObjectValue(const StringRef& str, const char* idString) +{ +} + +bool ObjectModel::SetShortEnumObjectValue(uint32_t val, const char* idString) +{ +} + +bool ObjectModel::SetBitmapObjectValue(uint32_t val, const char* idString) +{ +} + +bool ObjectModel::SetBoolObjectValue(bool val, const char* idString) +{ +} + +bool ObjectModel::AdjustFloatObjectValue(float val, const char* idString) +{ +} + +bool ObjectModel::AdjustUnsignedObjectValue(int32_t val, const char* idString) +{ +} + +bool ObjectModel::AdjustSignedObjectValue(int32_t val, const char* idString) +{ +} + +bool ObjectModel::ToggleBoolObjectValue(const char* idString) +{ +} +#endif + +const char** ObjectModel::GetStringObjectPointer(const char* idString) +{ + //TODO + return nullptr; +} + +uint32_t* ObjectModel::GetShortEnumObjectPointer(const char* idString) +{ + const ObjectModelTableEntry *e = FindObjectModelLeafEntry(idString); + return (e == nullptr) ? nullptr : (uint32_t*)(e->GetValuePointer(this, TYPE_OF(Enum32))); +} + +uint32_t* ObjectModel::GetBitmapObjectPointer(const char* idString) +{ + const ObjectModelTableEntry *e = FindObjectModelLeafEntry(idString); + return (e == nullptr) ? nullptr : (uint32_t*)(e->GetValuePointer(this, TYPE_OF(Bitmap32))); +} + +/*static*/ const char* ObjectModel::GetNextElement(const char *id) +{ + while (*id != 0 && *id != '.' && *id != '[') + { + ++id; + } + if (*id == '.') + { + ++id; + } + return id; +} + +bool ObjectModelTableEntry::Matches(const char* filterString, ObjectModelFilterFlags filterFlags) const +{ + return IdCompare(filterString) == 0 && (flags & filterFlags) == filterFlags; +} + +const ObjectModelTableEntry *ObjectModelTableEntry::FindLeafEntry(ObjectModel *self, const char *idString) const +{ + if (!IsObject()) + { + return this; + } + + return ((ObjectModel*)param(self))->FindObjectModelLeafEntry(ObjectModel::GetNextElement(idString)); +} + +// Add the value of this element to the buffer, returning true if it matched and we did +bool ObjectModelTableEntry::ReportAsJson(OutputBuffer* buf, ObjectModel *self, const char* filter, ObjectModel::ReportFlags flags) const +{ + //TODO handle arrays + buf->cat(name); + buf->cat(':'); + + if ((type & isArray) != 0) + { + //TODO + buf->cat("[]"); + } + else + { + void *nParam = param(self); + switch (type & 15) + { + case TYPE_OF(ObjectModel): + ((ObjectModel*)nParam)->ReportAsJson(buf, filter, flags); + break; + + case TYPE_OF(float): + buf->cat("%.1f", (double)*(const float *)nParam); //TODO different parameters need different number of decimal places + break; + + case TYPE_OF(uint32_t): + buf->cat("%" PRIu32, *(const uint32_t *)nParam); + break; + + case TYPE_OF(int32_t): + buf->cat("%" PRIi32, *(const int32_t *)nParam); + break; + + case TYPE_OF(Bitmap32): + if (flags & ObjectModel::shortForm) + { + buf->cat("%" PRIu32, *(const uint32_t *)nParam); + } + else + { + buf->cat('['); + // TODO list the bits that are set + buf->cat(']'); + } + break; + + case TYPE_OF(Enum32): + if (flags & ObjectModel::shortForm) + { + buf->cat("%" PRIu32, *(const uint32_t *)nParam); + } + else + { + buf->cat("\"unimplemented\""); + // TODO append the real name + } + break; + + case TYPE_OF(bool): + { + const bool bVal = *(const bool *)nParam; + if (flags & ObjectModel::shortForm) + { + buf->cat((bVal) ? '1' : '0'); + } + else + { + buf->cat((bVal) ? "yes" : "no"); + } + } + break; + } + } + + return true; +} + +// Compare and ID with the name of this object +int ObjectModelTableEntry::IdCompare(const char *id) const +{ + if (id[0] == '*') + { + return true; + } + + const char *n = name; + while (*id == *n && *n != 0) + { + ++id; + ++n; + } + return (*n == 0 && (*id == 0 || *id == '.' || *id == '[')) ? 0 + : (*id > *n) ? 1 + : -1; +} + +// Check the type is correct, call the function if necessary and return the pointer +void* ObjectModelTableEntry::GetValuePointer(ObjectModel *self, TypeCode t) const +{ + if (t != type) + { + return nullptr; + } + return param(self); +} + +#endif + +// End diff --git a/src/ObjectModel/ObjectModel.h b/src/ObjectModel/ObjectModel.h new file mode 100644 index 00000000..dddc6e21 --- /dev/null +++ b/src/ObjectModel/ObjectModel.h @@ -0,0 +1,160 @@ +/* + * ObjectModel.h + * + * Created on: 27 Aug 2018 + * Author: David + */ + +#ifndef SRC_OBJECTMODEL_OBJECTMODEL_H_ +#define SRC_OBJECTMODEL_OBJECTMODEL_H_ + +#include "RepRapFirmware.h" + +#ifdef SUPPORT_OBJECT_MODEL + +typedef uint32_t ObjectModelFilterFlags; +typedef uint8_t TypeCode; + +// Dummy types, used to define type codes +class Bitmap32 { }; +class Enum32 { }; + +// Forward declarations +class ObjectModelTableEntry; + +class ObjectModel +{ +public: + enum ReportFlags : uint16_t + { + shortForm = 1 + }; + + ObjectModel(); + + // Construct a JSON representation of those parts of the object model requested by the user + bool ReportAsJson(OutputBuffer *buf, const char *filter, ReportFlags rflags); + + // Get values of various types from the object model, returning true if successful + template<class T> bool GetObjectValue(T& val, const char *idString); + + bool GetStringObjectValue(const StringRef& str, const char* idString) const; + bool GetLongEnumObjectValue(const StringRef& str, const char *idString) const; + bool GetShortEnumObjectValue(uint32_t &val, const char *idString) const; + bool GetBitmapObjectValue(uint32_t &val, const char *idString) const; + + // Try to set values of various types from the object model, returning true if successful + bool SetFloatObjectValue(float val, const char *idString); + bool SetUnsignedObjectValue(uint32_t val, const char *idString); + bool SetSignedObjectValue(int32_t val, const char *idString); + bool SetStringObjectValue(const StringRef& str, const char *idString); + bool SetLongEnumObjectValue(const StringRef& str, const char *idString); + bool SetShortEnumObjectValue(uint32_t val, const char *idString); + bool SetBitmapObjectValue(uint32_t val, const char *idString); + bool SetBoolObjectValue(bool val, const char *idString); + + // Try to adjust values of various types from the object model, returning true if successful + bool AdjustFloatObjectValue(float val, const char *idString); + bool AdjustUnsignedObjectValue(int32_t val, const char *idString); + bool AdjustSignedObjectValue(int32_t val, const char *idString); + bool ToggleBoolObjectValue(const char *idString); + + // Get the object model table entry for the current level object in the query + const ObjectModelTableEntry *FindObjectModelTableEntry(const char *idString); + + // Get the object model table entry for the leaf object in the query + const ObjectModelTableEntry *FindObjectModelLeafEntry(const char *idString); + // Skip the current element in the ID or filter string + static const char* GetNextElement(const char *id); + +protected: + virtual const char *GetModuleName() const = 0; + virtual const ObjectModelTableEntry *GetObjectModelTable(size_t& numEntries) const = 0; + +private: + // Get pointers to various types from the object model, returning null if failed + template<class T> T* GetObjectPointer(const char* idString); + + const char **GetStringObjectPointer(const char *idString); + uint32_t *GetShortEnumObjectPointer(const char *idString); + uint32_t *GetBitmapObjectPointer(const char *idString); +}; + +// Function template used to get constexpr type IDs +template<class T> constexpr TypeCode TypeOf(); + +template<> constexpr TypeCode TypeOf<bool> () { return 1; } +template<> constexpr TypeCode TypeOf<uint32_t> () { return 2; } +template<> constexpr TypeCode TypeOf<int32_t>() { return 3; } +template<> constexpr TypeCode TypeOf<float>() { return 4; } +template<> constexpr TypeCode TypeOf<Bitmap32>() { return 5; } +template<> constexpr TypeCode TypeOf<Enum32>() { return 6; } +template<> constexpr TypeCode TypeOf<ObjectModel>() { return 7; } + +#define TYPE_OF(_t) (TypeOf<_t>()) + +// Object model table entry +// It must be possible to construct these in the form of initialised data in flash memory, to avoid using large amounts of RAM. +// Therefore we can't use a class hierarchy to represent different types of entry. Instead we use a type code and a void* parameter. +// Only const member functions are allowed in this class +class ObjectModelTableEntry +{ +public: + enum ObjectModelEntryFlags : uint16_t + { + none = 0, + live = 1, // fast changing data, included in common status response + canAlter = 2, // we can alter this value + isArray = 4 // value is an array of the basic type + }; + + typedef void *(*ParamFuncPtr_t)(ObjectModel*); + + // Return true if this object table entry matches a filter or query + bool Matches(const char *filter, ObjectModelFilterFlags flags) const; + + // Add the value of this element to the buffer, returning true if it matched and we did + bool ReportAsJson(OutputBuffer* buf, ObjectModel *self, const char* filter, ObjectModel::ReportFlags flags) const; + + const char* GetName() const { return name; } + + int IdCompare(const char *id) const; + + bool IsObject() const { return type == TYPE_OF(ObjectModel); } + const ObjectModelTableEntry *FindLeafEntry(ObjectModel *self, const char *idString) const; + + // Check the type is correct, call the function if necessary and return the pointer + void* GetValuePointer(ObjectModel *self, TypeCode t) const; + + // Note: all data members must be public so that we can brace-initialise these. This doesn't matter because they are always 'const'. + const char * name; + ParamFuncPtr_t param; + TypeCode type; + ObjectModelEntryFlags flags; +}; + +template<class T> bool ObjectModel::GetObjectValue(T& val, const char *idString) +{ + const ObjectModelTableEntry *e = FindObjectModelLeafEntry(idString); + if (e == nullptr) + { + return false; + } + const T *p = (float*)(e->GetValuePointer(this, TYPE_OF(T))); + if (p == nullptr) + { + return false; + } + val = *p; + return true; +} + +template<class T> T* ObjectModel::GetObjectPointer(const char* idString) +{ + const ObjectModelTableEntry *e = FindObjectModelLeafEntry(idString); + return (e == nullptr) ? nullptr : (T*)(e->GetValuePointer(this, TYPE_OF(T))); +} + +#endif + +#endif /* SRC_OBJECTMODEL_OBJECTMODEL_H_ */ diff --git a/src/Platform.cpp b/src/Platform.cpp index 2cf4eb56..eafcdc41 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -4466,7 +4466,7 @@ bool Platform::ConfigureStallDetection(GCodeBuffer& gb, const StringRef& reply) { if (IsBitSet(drivers, drive)) { - SmartDrivers::SetCoolStep(drive, coolStepConfig); + SmartDrivers::SetRegister(drive, SmartDriverRegister::coolStep, coolStepConfig); } } } diff --git a/src/RepRap.cpp b/src/RepRap.cpp index 76e18a59..9f28ae82 100644 --- a/src/RepRap.cpp +++ b/src/RepRap.cpp @@ -119,6 +119,33 @@ extern "C" void hsmciIdle(uint32_t stBits, uint32_t dmaBits) #endif +#ifdef SUPPORT_OBJECT_MODEL + +// Object model table and functions +// Note: if using GCC version 7.3.1 20180622 then if lambda functions are used in this table, you must compile this file with option -std=gnu++17. +// Otherwise the table will be allocate in RAM instead of flash, which wastes too much RAM. + +// Macro to build a standard lambda function that includes the necessary type conversions +#define OBJECT_MODEL_FUNC(_ret) [] (ObjectModel* arg) { RepRap * const self = static_cast<RepRap*>(arg); return (void *)(_ret); } + +const ObjectModelTableEntry RepRap::objectModelTable[] = +{ + { "gcodes", OBJECT_MODEL_FUNC(&(self->GetGCodes())), TYPE_OF(ObjectModel), ObjectModelTableEntry::none } +}; + +const char *RepRap::GetModuleName() const +{ + return nullptr; // this module has no name and doesn't need one +} + +const ObjectModelTableEntry *RepRap::GetObjectModelTable(size_t& numEntries) const +{ + numEntries = ARRAY_SIZE(objectModelTable); + return objectModelTable; +} + +#endif + // RepRap member functions. // Do nothing more in the constructor; put what you want in RepRap:Init() diff --git a/src/RepRap.h b/src/RepRap.h index 0f741126..89e49467 100644 --- a/src/RepRap.h +++ b/src/RepRap.h @@ -22,6 +22,7 @@ Licence: GPL #define REPRAP_H #include "RepRapFirmware.h" +#include "ObjectModel/ObjectModel.h" #include "MessageType.h" #include "RTOSIface.h" @@ -33,6 +34,9 @@ enum class ResponseSource }; class RepRap +#ifdef SUPPORT_OBJECT_MODEL + : public ObjectModel +#endif { public: RepRap(); @@ -121,6 +125,14 @@ public: void KickHeatTaskWatchdog() { heatTaskIdleTicks = 0; } #endif +#ifdef SUPPORT_OBJECT_MODEL +protected: + const char *GetModuleName() const override; + const ObjectModelTableEntry *GetObjectModelTable(size_t& numEntries) const override; + + static const ObjectModelTableEntry objectModelTable[]; +#endif + private: static void EncodeString(StringRef& response, const char* src, size_t spaceToLeave, bool allowControlChars = false, char prefix = 0); diff --git a/src/Version.h b/src/Version.h index 8eba899f..ea55aaa3 100644 --- a/src/Version.h +++ b/src/Version.h @@ -22,7 +22,7 @@ #endif #ifndef DATE -# define DATE "2018-08-27b1" +# define DATE "2018-08-29b1" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman, printm3d" |