From b251e6daff1e798ed722f7d20d356e1eddbd5a44 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 21 Dec 2011 14:41:06 -0700 Subject: [PATCH 01/61] Added Version menu item to the G4 Interface Panel to show the motherboard and extruder firmware versions. --- firmware/src/shared/Menu.cc | 64 ++++++++++++++++++++++++++++++++++++- firmware/src/shared/Menu.hh | 15 +++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 397dd19..2aeac22 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -12,6 +12,7 @@ #include "Timeout.hh" #include "InterfaceBoard.hh" #include "Interface.hh" +#include "Version.hh" #include #include #include "SDCard.hh" @@ -429,6 +430,59 @@ void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } +void VersionMode::reset() { +} + +void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { + static PROGMEM prog_uchar version1[] = "Firmware Version"; + static PROGMEM prog_uchar version2[] = "----------------"; + static PROGMEM prog_uchar version3[] = "Motherboard: _._"; + static PROGMEM prog_uchar version4[] = " Extruder: _._"; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(version1); + + lcd.setCursor(0,1); + lcd.writeFromPgmspace(version2); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(version3); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(version4); + + //Display the motherboard version + lcd.setCursor(13, 2); + lcd.writeInt(firmware_version / 100, 1); + + lcd.setCursor(15, 2); + lcd.writeInt(firmware_version % 100, 1); + + //Display the extruder version + OutPacket responsePacket; + + if (queryExtruderParameter(SLAVE_CMD_VERSION, responsePacket)) { + uint16_t extruderVersion = responsePacket.read16(1); + + lcd.setCursor(13, 3); + lcd.writeInt(extruderVersion / 100, 1); + + lcd.setCursor(15, 3); + lcd.writeInt(extruderVersion % 100, 1); + } else { + lcd.setCursor(13, 3); + lcd.writeString("X.X"); + } + } else { + } +} + +void VersionMode::notifyButtonPressed(ButtonArray::ButtonName button) { + interface::popScreen(); +} void Menu::update(LiquidCrystal& lcd, bool forceRedraw) { static PROGMEM prog_uchar blankLine[] = " "; @@ -560,7 +614,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 5; + itemCount = 6; reset(); } @@ -568,6 +622,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { static PROGMEM prog_uchar monitor[] = "Monitor Mode"; static PROGMEM prog_uchar build[] = "Build from SD"; static PROGMEM prog_uchar jog[] = "Jog Mode"; + static PROGMEM prog_uchar versions[] = "Version"; static PROGMEM prog_uchar snake[] = "Snake Game"; switch (index) { @@ -584,6 +639,9 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { // blank break; case 4: + lcd.writeFromPgmspace(versions); + break; + case 5: lcd.writeFromPgmspace(snake); break; } @@ -604,6 +662,10 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&jogger); break; case 4: + // Show build from SD screen + interface::pushScreen(&versionMode); + break; + case 5: // Show build from SD screen interface::pushScreen(&snake); break; diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index facc93c..81572c5 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -204,6 +204,20 @@ public: }; +class VersionMode: public Screen { +private: + +public: + micros_t getUpdateRate() {return 500L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + + class MainMenu: public Menu { public: MainMenu(); @@ -218,6 +232,7 @@ private: MonitorMode monitorMode; SDMenu sdMenu; JogMode jogger; + VersionMode versionMode; SnakeMode snake; }; From 3e79289309a084b076115d9bfeb2c513401bd1f4 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Thu, 22 Dec 2011 10:52:57 -0700 Subject: [PATCH 02/61] Added CONT to SHORT/LONG in JogMode for the Gen 4 Interface whilst a key is held down. This mirrors Continuous Mode in ReplicatorG and saves from having to repeatedly hit a button to move around. --- .../Motherboard/boards/mb24/ButtonArray.cc | 18 +++++++++ firmware/src/shared/ButtonArray.hh | 3 ++ firmware/src/shared/Interface.cc | 4 ++ firmware/src/shared/Interface.hh | 3 ++ firmware/src/shared/InterfaceBoard.cc | 8 ++++ firmware/src/shared/InterfaceBoard.hh | 4 ++ firmware/src/shared/Menu.cc | 37 ++++++++++++++++--- firmware/src/shared/Menu.hh | 2 + 8 files changed, 74 insertions(+), 5 deletions(-) diff --git a/firmware/src/Motherboard/boards/mb24/ButtonArray.cc b/firmware/src/Motherboard/boards/mb24/ButtonArray.cc index 8fda342..e9c9d2b 100644 --- a/firmware/src/Motherboard/boards/mb24/ButtonArray.cc +++ b/firmware/src/Motherboard/boards/mb24/ButtonArray.cc @@ -74,3 +74,21 @@ bool ButtonArray::getButton(ButtonName& button) { return buttonValid; } + + +bool ButtonArray::isButtonPressed(ButtonArray::ButtonName button) { + uint8_t newL = PINL;// & 0xFE; + uint8_t newC = PINC;// & 0x06; + + uint8_t keys = newL; + uint8_t i = button; + if ( button >= 10 ) { + keys = newC; + i = i - 10; + } + + //Keys are active low, invert + keys = ~ keys; + + return (keys & (1 << i)) == (1 << i); +} diff --git a/firmware/src/shared/ButtonArray.hh b/firmware/src/shared/ButtonArray.hh index 699232b..e8c8f71 100644 --- a/firmware/src/shared/ButtonArray.hh +++ b/firmware/src/shared/ButtonArray.hh @@ -42,6 +42,9 @@ public: void scanButtons(); bool getButton(ButtonName& button); + + //Returns true if button is depressed + bool isButtonPressed(ButtonArray::ButtonName button); }; diff --git a/firmware/src/shared/Interface.cc b/firmware/src/shared/Interface.cc index 902c9aa..3cc2137 100644 --- a/firmware/src/shared/Interface.cc +++ b/firmware/src/shared/Interface.cc @@ -52,6 +52,10 @@ void popScreen() { board->popScreen(); } +bool isButtonPressed(ButtonArray::ButtonName button) { + return board->isButtonPressed(button); +} + void doInterrupt() { board->doInterrupt(); } diff --git a/firmware/src/shared/Interface.hh b/firmware/src/shared/Interface.hh index 438e365..a9bdaac 100644 --- a/firmware/src/shared/Interface.hh +++ b/firmware/src/shared/Interface.hh @@ -43,6 +43,9 @@ void pushScreen(Screen* newScreen); /// it will not be removed. void popScreen(); +/// This is called for a specific button and returns true if the button +/// is currently depressed +bool isButtonPressed(ButtonArray::ButtonName button); /// Screen update interrupt. This scans the keypad to look for any changes. To /// ensure a consistant user response, it should be called from a medium frequency diff --git a/firmware/src/shared/InterfaceBoard.cc b/firmware/src/shared/InterfaceBoard.cc index ea6cfeb..ed926e1 100644 --- a/firmware/src/shared/InterfaceBoard.cc +++ b/firmware/src/shared/InterfaceBoard.cc @@ -78,6 +78,14 @@ void InterfaceBoard::doUpdate() { screenStack[screenIndex]->update(lcd, false); } +bool InterfaceBoard::isButtonPressed(ButtonArray::ButtonName button) { + bool buttonPressed = buttons.isButtonPressed(button); + + if ( buttonPressed ) screenStack[screenIndex]->notifyButtonPressed(button); + + return buttonPressed; +} + void InterfaceBoard::pushScreen(Screen* newScreen) { if (screenIndex < SCREEN_STACK_DEPTH - 1) { screenIndex++; diff --git a/firmware/src/shared/InterfaceBoard.hh b/firmware/src/shared/InterfaceBoard.hh index d8f6a8d..a57dbf0 100644 --- a/firmware/src/shared/InterfaceBoard.hh +++ b/firmware/src/shared/InterfaceBoard.hh @@ -85,6 +85,10 @@ public: /// service the button input pad. void doInterrupt(); + /// This is called for a specific button and returns true if the + /// button is currently depressed + bool isButtonPressed(ButtonArray::ButtonName button); + /// Add a new screen to the stack. This automatically calls reset() /// and then update() on the screen, to ensure that it displays /// properly. If there are more than SCREEN_STACK_DEPTH screens already diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 2aeac22..865cd30 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -105,6 +105,7 @@ void SplashScreen::reset() { void JogMode::reset() { jogDistance = DISTANCE_SHORT; distanceChanged = false; + lastDirectionButtonPressed = (ButtonArray::ButtonName)0; } void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { @@ -115,6 +116,7 @@ void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { static PROGMEM prog_uchar distanceShort[] = "SHORT"; static PROGMEM prog_uchar distanceLong[] = "LONG"; + static PROGMEM prog_uchar distanceCont[] = "CONT"; if (forceRedraw || distanceChanged) { lcd.clear(); @@ -128,6 +130,9 @@ void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { case DISTANCE_LONG: lcd.writeFromPgmspace(distanceLong); break; + case DISTANCE_CONT: + lcd.writeFromPgmspace(distanceCont); + break; } lcd.setCursor(0,1); @@ -141,6 +146,14 @@ void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { distanceChanged = false; } + + if ( jogDistance == DISTANCE_CONT ) { + if ( lastDirectionButtonPressed ) { + if (interface::isButtonPressed(lastDirectionButtonPressed)) + JogMode::notifyButtonPressed(lastDirectionButtonPressed); + else lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + } + } } void JogMode::jog(ButtonArray::ButtonName direction) { @@ -149,6 +162,8 @@ void JogMode::jog(ButtonArray::ButtonName direction) { int32_t interval = 2000; uint8_t steps; + if ( jogDistance == DISTANCE_CONT ) interval = 1000; + switch(jogDistance) { case DISTANCE_SHORT: steps = 20; @@ -156,6 +171,9 @@ void JogMode::jog(ButtonArray::ButtonName direction) { case DISTANCE_LONG: steps = 200; break; + case DISTANCE_CONT: + steps = 50; + break; } switch(direction) { @@ -179,6 +197,9 @@ void JogMode::jog(ButtonArray::ButtonName direction) { break; } + if ( jogDistance == DISTANCE_CONT ) lastDirectionButtonPressed = direction; + else lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + steppers::setTarget(position, interval); } @@ -186,11 +207,17 @@ void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { switch (button) { case ButtonArray::ZERO: case ButtonArray::OK: - if (jogDistance == DISTANCE_SHORT) { - jogDistance = DISTANCE_LONG; - } - else { - jogDistance = DISTANCE_SHORT; + switch(jogDistance) + { + case DISTANCE_SHORT: + jogDistance = DISTANCE_LONG; + break; + case DISTANCE_LONG: + jogDistance = DISTANCE_CONT; + break; + case DISTANCE_CONT: + jogDistance = DISTANCE_SHORT; + break; } distanceChanged = true; break; diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 81572c5..1e43b02 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -95,10 +95,12 @@ private: enum distance_t { DISTANCE_SHORT, DISTANCE_LONG, + DISTANCE_CONT, }; distance_t jogDistance; bool distanceChanged; + ButtonArray::ButtonName lastDirectionButtonPressed; void jog(ButtonArray::ButtonName direction); From c5e06032d7e011a684891e976992fece2e5f27ae Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Thu, 22 Dec 2011 21:16:41 -0700 Subject: [PATCH 03/61] Adds Elapsed Time, % Complete and Estimated TimeLeft to builds from SD Card. These changes are improvements to commit 88d02da and de120a2 by revarbat, and are created from that work. This commit is meant as a replacement. Credit for the original code goes to revarbat. Additions made to revarbat's commit are: 1. Bug fix, elapsed time now stops increasing when the print finishes. 2. Bug fix, firmware rrmbv12 now compiles 3. Added Estimated Time Left which gets calculated after 1% of the build has completed. 4. Elapsed Time, % Complete and Estimated TimeLeft set on a timed rotate --- firmware/src/Motherboard/Host.cc | 5 + firmware/src/Motherboard/Host.hh | 2 + firmware/src/Motherboard/SDCard.cc | 28 ++ firmware/src/Motherboard/SDCard.hh | 3 + .../Motherboard/boards/mb24/Motherboard.cc | 24 ++ .../Motherboard/boards/mb24/Motherboard.hh | 4 + .../Motherboard/boards/rrmbv12/Motherboard.cc | 5 + .../Motherboard/boards/rrmbv12/Motherboard.hh | 1 + firmware/src/shared/Menu.cc | 244 +++++++++++++++++- firmware/src/shared/Menu.hh | 5 +- firmware/src/shared/Types.hh | 1 + 11 files changed, 316 insertions(+), 6 deletions(-) diff --git a/firmware/src/Motherboard/Host.cc b/firmware/src/Motherboard/Host.cc index 6a57fec..1a35c69 100644 --- a/firmware/src/Motherboard/Host.cc +++ b/firmware/src/Motherboard/Host.cc @@ -579,6 +579,11 @@ void stopBuild() { do_host_reset = true; // indicate reset after response has been sent } +bool isBuildComplete() { + if (( command::isEmpty() ) && ( ! sdcard::playbackHasNext() )) return true; + return false; +} + } /* footnote 1: due to a protocol change, replicatiorG 0026 and newer can ONLY work with diff --git a/firmware/src/Motherboard/Host.hh b/firmware/src/Motherboard/Host.hh index 1d13327..dba0f3d 100644 --- a/firmware/src/Motherboard/Host.hh +++ b/firmware/src/Motherboard/Host.hh @@ -63,6 +63,8 @@ sdcard::SdErrorCode startBuildFromSD(); /// Stop the current build void stopBuild(); +/// Returns true if the build is completed +bool isBuildComplete(); } #endif // HOST_HH_ diff --git a/firmware/src/Motherboard/SDCard.cc b/firmware/src/Motherboard/SDCard.cc index 5c43ac9..a9f9aab 100644 --- a/firmware/src/Motherboard/SDCard.cc +++ b/firmware/src/Motherboard/SDCard.cc @@ -23,6 +23,7 @@ #include "lib_sd/fat.h" #include "lib_sd/sd_raw.h" #include "lib_sd/partition.h" +#include "Motherboard.hh" #ifndef USE_DYNAMIC_MEMORY #error Dynamic memory should be explicitly disabled in the G3 mobo. @@ -190,6 +191,9 @@ bool createFile(char *name) bool capturing = false; bool playing = false; uint32_t capturedBytes = 0L; +uint32_t countupBytes = 0L; +uint32_t percentBytes = 0L; +uint8_t percentPlayed = 0L; bool isPlaying() { return playing; @@ -207,6 +211,7 @@ SdErrorCode startCapture(char* filename) return result; } capturedBytes = 0L; + countupBytes = 0L; file = 0; // Always operate in truncation mode. deleteFile(filename); @@ -255,6 +260,11 @@ bool has_more; void fetchNextByte() { int16_t read = fat_read_file(file, &next_byte, 1); has_more = read > 0; + countupBytes++; + if (countupBytes >= percentBytes) { + countupBytes -= percentBytes; + percentPlayed++; + } } bool playbackHasNext() { @@ -275,15 +285,33 @@ SdErrorCode startPlayback(char* filename) { return result; } capturedBytes = 0L; + + countupBytes = 0L; + file = 0; if (!openFile(filename, &file) || file == 0) { return SD_ERR_FILE_NOT_FOUND; } playing = true; + + percentPlayed = 0; + percentBytes = 0L; + int32_t off = 0L; + fat_seek_file(file, &off, FAT_SEEK_END); + percentBytes = off / 100L; + off = 0L; + fat_seek_file(file, &off, FAT_SEEK_SET); + + Motherboard::getBoard().resetCurrentSeconds(); + fetchNextByte(); return SD_SUCCESS; } +uint8_t getPercentPlayed() { + return percentPlayed; +} + void playbackRewind(uint8_t bytes) { int32_t offset = -((int32_t)bytes); fat_seek_file(file, &offset, FAT_SEEK_CUR); diff --git a/firmware/src/Motherboard/SDCard.hh b/firmware/src/Motherboard/SDCard.hh index 01da31c..f3c4975 100644 --- a/firmware/src/Motherboard/SDCard.hh +++ b/firmware/src/Motherboard/SDCard.hh @@ -87,6 +87,9 @@ namespace sdcard { SdErrorCode startPlayback(char* filename); + /// Return the percentage of the file printed. + uint8_t getPercentPlayed(); + /// See if there is more data available in the playback file. /// \return True if there is more data in the file bool playbackHasNext(); diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index f829190..fdf0a5e 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -163,12 +163,36 @@ micros_t Motherboard::getCurrentMicros() { } +/// Get the number of seconds that have passed since +/// the board was booted or the timer reset. +micros_t Motherboard::getCurrentSeconds() { + micros_t seconds_snapshot; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + seconds_snapshot = seconds; + } + return seconds_snapshot; +} + + +/// Reset the seconds counter to 0. +void Motherboard::resetCurrentSeconds() { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + seconds = 0L; + } +} + + /// Run the motherboard interrupt void Motherboard::doInterrupt() { if (hasInterfaceBoard) { interfaceBoard.doInterrupt(); } micros += INTERVAL_IN_MICROSECONDS; + countupMicros += INTERVAL_IN_MICROSECONDS; + while (countupMicros > 1000000L) { + seconds += 1; + countupMicros -= 1000000L; + } // Do not move steppers if the board is in a paused state if (command::isPaused()) return; steppers::doInterrupt(); diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.hh b/firmware/src/Motherboard/boards/mb24/Motherboard.hh index 59326de..dcac51c 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.hh +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.hh @@ -49,6 +49,8 @@ private: /// Microseconds since board initialization volatile micros_t micros; + volatile micros_t countupMicros; + volatile seconds_t seconds; /// Private constructor; use the singleton Motherboard(); @@ -87,6 +89,8 @@ public: /// the board was initialized. This value will wrap after /// 2**32 microseconds (ca. 70 minutes); callers should compensate for this. micros_t getCurrentMicros(); + micros_t getCurrentSeconds(); + void resetCurrentSeconds(); /// Write an error code to the debug pin. void indicateError(int errorCode); diff --git a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc index 98fe3bb..0a95477 100644 --- a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc +++ b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc @@ -146,6 +146,11 @@ micros_t Motherboard::getCurrentMicros() { void Motherboard::runMotherboardSlice() { } +/// Do nothing, need this to stop compile error as it called from SDCard +/// for firmware mb24 +void Motherboard::resetCurrentSeconds() { +} + /// Run the motherboard interrupt void Motherboard::doInterrupt() { micros += INTERVAL_IN_MICROSECONDS; diff --git a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh index a151486..01cfe1b 100644 --- a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh +++ b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh @@ -64,6 +64,7 @@ public: /// the board was initialized. This value will wrap after /// 2**16 microseconds; callers should compensate for this. micros_t getCurrentMicros(); + void resetCurrentSeconds(); /// Write an error code to the debug pin. void indicateError(int errorCode); diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 865cd30..571aea6 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -12,6 +12,7 @@ #include "Timeout.hh" #include "InterfaceBoard.hh" #include "Interface.hh" +#include "Motherboard.hh" #include "Version.hh" #include #include @@ -69,6 +70,163 @@ bool queryExtruderParameter(uint8_t parameter, OutPacket& responsePacket) { return true; } + + +void strcat(char *buf, const char* str) +{ + char *ptr = buf; + while (*ptr) ptr++; + while (*str) *ptr++ = *str++; + *ptr++ = '\0'; +} + + +int appendTime(char *buf, uint8_t buflen, uint32_t val) +{ + bool hasdigit = false; + uint8_t idx = 0; + uint8_t written = 0; + + if (buflen < 1) { + return written; + } + + while (idx < buflen && buf[idx]) idx++; + if (idx >= buflen-1) { + buf[buflen-1] = '\0'; + return written; + } + + uint8_t radidx = 0; + const uint8_t radixcount = 5; + const uint8_t houridx = 2; + const uint8_t minuteidx = 4; + uint32_t radixes[radixcount] = {360000, 36000, 3600, 600, 60}; + if (val >= 3600000) { + val %= 3600000; + } + for (radidx = 0; radidx < radixcount; radidx++) { + char digit = '0'; + uint8_t bit = 8; + uint32_t radshift = radixes[radidx] << 3; + for (; bit > 0; bit >>= 1, radshift >>= 1) { + if (val > radshift) { + val -= radshift; + digit += bit; + } + } + if (hasdigit || digit != '0' || radidx >= houridx) { + buf[idx++] = digit; + hasdigit = true; + } else { + buf[idx++] = ' '; + } + if (idx >= buflen) { + buf[buflen-1] = '\0'; + return written; + } + written++; + if (radidx == houridx) { + buf[idx++] = 'h'; + if (idx >= buflen) { + buf[buflen-1] = '\0'; + return written; + } + written++; + } + if (radidx == minuteidx) { + buf[idx++] = 'm'; + if (idx >= buflen) { + buf[buflen-1] = '\0'; + return written; + } + written++; + } + } + + if (idx < buflen) { + buf[idx] = '\0'; + } else { + buf[buflen-1] = '\0'; + } + + return written; +} + + + +int appendUint8(char *buf, uint8_t buflen, uint8_t val) +{ + bool hasdigit = false; + uint8_t written = 0; + uint8_t idx = 0; + + if (buflen < 1) { + return written; + } + + while (idx < buflen && buf[idx]) idx++; + if (idx >= buflen-1) { + buf[buflen-1] = '\0'; + return written; + } + + if (val >= 100) { + uint8_t res = val / 100; + val -= res * 100; + buf[idx++] = '0' + res; + if (idx >= buflen) { + buf[buflen-1] = '\0'; + return written; + } + hasdigit = true; + written++; + } else { + buf[idx++] = ' '; + if (idx >= buflen) { + buf[buflen-1] = '\0'; + return written; + } + written++; + } + + if (val >= 10 || hasdigit) { + uint8_t res = val / 10; + val -= res * 10; + buf[idx++] = '0' + res; + if (idx >= buflen) { + buf[buflen-1] = '\0'; + return written; + } + hasdigit = true; + written++; + } else { + buf[idx++] = ' '; + if (idx >= buflen) { + buf[buflen-1] = '\0'; + return written; + } + written++; + } + + buf[idx++] = '0' + val; + if (idx >= buflen) { + buf[buflen-1] = '\0'; + return written; + } + written++; + + if (idx < buflen) { + buf[idx] = '\0'; + } else { + buf[buflen-1] = '\0'; + } + + return written; +} + + + void SplashScreen::update(LiquidCrystal& lcd, bool forceRedraw) { static PROGMEM prog_uchar splash1[] = " "; static PROGMEM prog_uchar splash2[] = " Thing-O-Matic "; @@ -359,11 +517,20 @@ void SnakeMode::notifyButtonPressed(ButtonArray::ButtonName button) { void MonitorMode::reset() { updatePhase = 0; + buildTimePhase = 0; + extruderStartSeconds = 0; } void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar extruder_temp[] = "Tool: ---/---C"; - static PROGMEM prog_uchar platform_temp[] = "Bed: ---/---C"; + static PROGMEM prog_uchar extruder_temp[] = "Tool: ---/---C"; + static PROGMEM prog_uchar platform_temp[] = "Bed: ---/---C"; + static PROGMEM prog_uchar elapsed_time[] = "Elapsed: 0h00m"; + static PROGMEM prog_uchar completed_percent[] = "Completed: 0%"; + static PROGMEM prog_uchar time_left[] = "TimeLeft: 0h00m"; + static PROGMEM prog_uchar time_left_calc[] = " calc.."; + static PROGMEM prog_uchar time_left_1min[] = " <1min"; + static PROGMEM prog_uchar time_left_none[] = " none"; + char buf[17]; if (forceRedraw) { lcd.clear(); @@ -375,6 +542,8 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { case host::HOST_STATE_BUILDING: case host::HOST_STATE_BUILDING_FROM_SD: lcd.writeString(host::getBuildName()); + lcd.setCursor(0,1); + lcd.writeFromPgmspace(completed_percent); break; case host::HOST_STATE_ERROR: lcd.writeString("error!"); @@ -386,8 +555,6 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,3); lcd.writeFromPgmspace(platform_temp); - - } else { } @@ -434,10 +601,77 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeString("XXX"); } break; + case 4: + enum host::HostState hostState = host::getHostState(); + + if ( (hostState != host::HOST_STATE_BUILDING ) && ( hostState != host::HOST_STATE_BUILDING_FROM_SD )) break; + + seconds_t secs; + + switch (buildTimePhase) { + case 0: + lcd.setCursor(0,1); + lcd.writeFromPgmspace(completed_percent); + lcd.setCursor(11,1); + buf[0] = '\0'; + appendUint8(buf, sizeof(buf), sdcard::getPercentPlayed()); + strcat(buf, "% "); + lcd.writeString(buf); + break; + case 1: + lcd.setCursor(0,1); + lcd.writeFromPgmspace(elapsed_time); + lcd.setCursor(9,1); + buf[0] = '\0'; + + if ( host::isBuildComplete() ) secs = lastElapsedSeconds; //We stop counting elapsed seconds when we are done + else { + lastElapsedSeconds = Motherboard::getBoard().getCurrentSeconds(); + secs = lastElapsedSeconds; + } + appendTime(buf, sizeof(buf), (uint32_t)secs); + lcd.writeString(buf); + break; + case 2: + lcd.setCursor(0,1); + lcd.writeFromPgmspace(time_left); + lcd.setCursor(9,1); + + if (( sdcard::getPercentPlayed() >= 1 ) && ( extruderStartSeconds )) { + buf[0] = '\0'; + seconds_t currentSeconds = Motherboard::getBoard().getCurrentSeconds() - extruderStartSeconds; + float secsf = (((float)currentSeconds / (float)sdcard::getPercentPlayed()) * 100.0 ) + - (float)currentSeconds; + + if ((secsf > 0.0 ) && (secsf < 60.0)) + lcd.writeFromPgmspace(time_left_1min); + else if (( secsf <= 0.0) || ( host::isBuildComplete() )) + lcd.writeFromPgmspace(time_left_none); + else { + appendTime(buf, sizeof(buf), (uint32_t)secsf); + lcd.writeString(buf); + } + } + else lcd.writeFromPgmspace(time_left_calc); + + //Set extruderStartSeconds to when the extruder starts extruding, so we can + //get an accurate TimeLeft: + if ( ! extruderStartSeconds ) { + if (queryExtruderParameter(SLAVE_CMD_GET_MOTOR_1_PWM, responsePacket)) { + uint8_t pwm = responsePacket.read8(1); + if ( pwm ) extruderStartSeconds = Motherboard::getBoard().getCurrentSeconds(); + } + } + break; + } + + buildTimePhase ++; + if ( buildTimePhase >= 3 ) buildTimePhase = 0; + break; } updatePhase++; - if (updatePhase > 3) { + if (updatePhase > 4) { updatePhase = 0; } } diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 1e43b02..e43d484 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -193,7 +193,10 @@ class MonitorMode: public Screen { private: CancelBuildMenu cancelBuildMenu; - uint8_t updatePhase; + uint8_t updatePhase; + uint8_t buildTimePhase; + seconds_t lastElapsedSeconds; + seconds_t extruderStartSeconds; public: micros_t getUpdateRate() {return 500L * 1000L;} diff --git a/firmware/src/shared/Types.hh b/firmware/src/shared/Types.hh index ffe85cf..820668e 100644 --- a/firmware/src/shared/Types.hh +++ b/firmware/src/shared/Types.hh @@ -22,5 +22,6 @@ /// Type used to store measurements of microseconds. typedef uint32_t micros_t; +typedef uint32_t seconds_t; #endif // TYPES_HH_ From 9da86f0125028321c68cc6e23f94dfe58c4a49a1 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 23 Dec 2011 18:25:05 -0700 Subject: [PATCH 04/61] Improvements to TimeLeft estimation, by using floating point. This goes with commit c5e0603 --- firmware/src/Motherboard/SDCard.cc | 27 ++++++++--------- firmware/src/Motherboard/SDCard.hh | 2 +- .../Motherboard/boards/mb24/Motherboard.cc | 8 +++-- .../Motherboard/boards/mb24/Motherboard.hh | 2 +- firmware/src/shared/Menu.cc | 30 ++++++++++--------- firmware/src/shared/Menu.hh | 9 +++--- 6 files changed, 40 insertions(+), 38 deletions(-) diff --git a/firmware/src/Motherboard/SDCard.cc b/firmware/src/Motherboard/SDCard.cc index a9f9aab..b12b8c4 100644 --- a/firmware/src/Motherboard/SDCard.cc +++ b/firmware/src/Motherboard/SDCard.cc @@ -190,10 +190,9 @@ bool createFile(char *name) bool capturing = false; bool playing = false; +int32_t fileSizeBytes = 0L; +int32_t playedBytes = 0L; uint32_t capturedBytes = 0L; -uint32_t countupBytes = 0L; -uint32_t percentBytes = 0L; -uint8_t percentPlayed = 0L; bool isPlaying() { return playing; @@ -211,7 +210,7 @@ SdErrorCode startCapture(char* filename) return result; } capturedBytes = 0L; - countupBytes = 0L; + playedBytes = 0L; file = 0; // Always operate in truncation mode. deleteFile(filename); @@ -260,11 +259,7 @@ bool has_more; void fetchNextByte() { int16_t read = fat_read_file(file, &next_byte, 1); has_more = read > 0; - countupBytes++; - if (countupBytes >= percentBytes) { - countupBytes -= percentBytes; - percentPlayed++; - } + playedBytes++; } bool playbackHasNext() { @@ -286,7 +281,7 @@ SdErrorCode startPlayback(char* filename) { } capturedBytes = 0L; - countupBytes = 0L; + playedBytes = 0L; file = 0; if (!openFile(filename, &file) || file == 0) { @@ -294,11 +289,9 @@ SdErrorCode startPlayback(char* filename) { } playing = true; - percentPlayed = 0; - percentBytes = 0L; int32_t off = 0L; fat_seek_file(file, &off, FAT_SEEK_END); - percentBytes = off / 100L; + fileSizeBytes = off; off = 0L; fat_seek_file(file, &off, FAT_SEEK_SET); @@ -308,8 +301,12 @@ SdErrorCode startPlayback(char* filename) { return SD_SUCCESS; } -uint8_t getPercentPlayed() { - return percentPlayed; +float getPercentPlayed() { + float percentPlayed = (float)(playedBytes * 100) / (float)fileSizeBytes; + + if ( percentPlayed > 100.0 ) return 100.0; + else if ( percentPlayed < 0.0 ) return 0.0; + else return percentPlayed; } void playbackRewind(uint8_t bytes) { diff --git a/firmware/src/Motherboard/SDCard.hh b/firmware/src/Motherboard/SDCard.hh index f3c4975..fd84e6b 100644 --- a/firmware/src/Motherboard/SDCard.hh +++ b/firmware/src/Motherboard/SDCard.hh @@ -88,7 +88,7 @@ namespace sdcard { /// Return the percentage of the file printed. - uint8_t getPercentPlayed(); + float getPercentPlayed(); /// See if there is more data available in the playback file. /// \return True if there is more data in the file diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index fdf0a5e..fbcc1af 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -165,12 +165,14 @@ micros_t Motherboard::getCurrentMicros() { /// Get the number of seconds that have passed since /// the board was booted or the timer reset. -micros_t Motherboard::getCurrentSeconds() { +float Motherboard::getCurrentSeconds() { micros_t seconds_snapshot; + micros_t countupMicros_snapshot; ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { - seconds_snapshot = seconds; + countupMicros_snapshot = countupMicros; + seconds_snapshot = seconds; } - return seconds_snapshot; + return (float)seconds_snapshot + ((float)countupMicros_snapshot / (float)1000000); } diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.hh b/firmware/src/Motherboard/boards/mb24/Motherboard.hh index dcac51c..6254b8f 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.hh +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.hh @@ -89,7 +89,7 @@ public: /// the board was initialized. This value will wrap after /// 2**32 microseconds (ca. 70 minutes); callers should compensate for this. micros_t getCurrentMicros(); - micros_t getCurrentSeconds(); + float getCurrentSeconds(); void resetCurrentSeconds(); /// Write an error code to the debug pin. diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 571aea6..0d8374f 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -518,14 +518,16 @@ void SnakeMode::notifyButtonPressed(ButtonArray::ButtonName button) { void MonitorMode::reset() { updatePhase = 0; buildTimePhase = 0; - extruderStartSeconds = 0; + buildComplete = false; + extruderStartSeconds = 0.0; + lastElapsedSeconds = 0.0; } void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { static PROGMEM prog_uchar extruder_temp[] = "Tool: ---/---C"; static PROGMEM prog_uchar platform_temp[] = "Bed: ---/---C"; static PROGMEM prog_uchar elapsed_time[] = "Elapsed: 0h00m"; - static PROGMEM prog_uchar completed_percent[] = "Completed: 0%"; + static PROGMEM prog_uchar completed_percent[] = "Completed: 0% "; static PROGMEM prog_uchar time_left[] = "TimeLeft: 0h00m"; static PROGMEM prog_uchar time_left_calc[] = " calc.."; static PROGMEM prog_uchar time_left_1min[] = " <1min"; @@ -606,7 +608,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( (hostState != host::HOST_STATE_BUILDING ) && ( hostState != host::HOST_STATE_BUILDING_FROM_SD )) break; - seconds_t secs; + float secs; switch (buildTimePhase) { case 0: @@ -614,7 +616,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeFromPgmspace(completed_percent); lcd.setCursor(11,1); buf[0] = '\0'; - appendUint8(buf, sizeof(buf), sdcard::getPercentPlayed()); + appendUint8(buf, sizeof(buf), (uint8_t)sdcard::getPercentPlayed()); strcat(buf, "% "); lcd.writeString(buf); break; @@ -637,18 +639,18 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeFromPgmspace(time_left); lcd.setCursor(9,1); - if (( sdcard::getPercentPlayed() >= 1 ) && ( extruderStartSeconds )) { + if (( sdcard::getPercentPlayed() >= 1.0 ) && ( extruderStartSeconds > 0.0)) { buf[0] = '\0'; - seconds_t currentSeconds = Motherboard::getBoard().getCurrentSeconds() - extruderStartSeconds; - float secsf = (((float)currentSeconds / (float)sdcard::getPercentPlayed()) * 100.0 ) - - (float)currentSeconds; + float currentSeconds = Motherboard::getBoard().getCurrentSeconds() - extruderStartSeconds; + secs = ((currentSeconds / sdcard::getPercentPlayed()) * 100.0 ) - currentSeconds; - if ((secsf > 0.0 ) && (secsf < 60.0)) + if ((secs > 0.0 ) && (secs < 60.0) && ( ! buildComplete ) ) lcd.writeFromPgmspace(time_left_1min); - else if (( secsf <= 0.0) || ( host::isBuildComplete() )) + else if (( secs <= 0.0) || ( host::isBuildComplete() ) || ( buildComplete ) ) { + buildComplete = true; lcd.writeFromPgmspace(time_left_none); - else { - appendTime(buf, sizeof(buf), (uint32_t)secsf); + } else { + appendTime(buf, sizeof(buf), (uint32_t)secs); lcd.writeString(buf); } } @@ -656,7 +658,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { //Set extruderStartSeconds to when the extruder starts extruding, so we can //get an accurate TimeLeft: - if ( ! extruderStartSeconds ) { + if ( extruderStartSeconds == 0.0 ) { if (queryExtruderParameter(SLAVE_CMD_GET_MOTOR_1_PWM, responsePacket)) { uint8_t pwm = responsePacket.read8(1); if ( pwm ) extruderStartSeconds = Motherboard::getBoard().getCurrentSeconds(); @@ -664,7 +666,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { } break; } - + buildTimePhase ++; if ( buildTimePhase >= 3 ) buildTimePhase = 0; break; diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index e43d484..c75db4b 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -193,10 +193,11 @@ class MonitorMode: public Screen { private: CancelBuildMenu cancelBuildMenu; - uint8_t updatePhase; - uint8_t buildTimePhase; - seconds_t lastElapsedSeconds; - seconds_t extruderStartSeconds; + uint8_t updatePhase; + uint8_t buildTimePhase; + float lastElapsedSeconds; + float extruderStartSeconds; + bool buildComplete; //For solving floating point rounding issues public: micros_t getUpdateRate() {return 500L * 1000L;} From dea413c1bd116147ce0a8124f84d3dec46e2f019 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 23 Dec 2011 19:45:33 -0700 Subject: [PATCH 05/61] Changes to commit 8ac6524 by revarbat. This commit is meant as a replacement. Credit for the original code goes to revarbat. 1. Changed name from "Heaters" to "Preheat" to mirror Control Panel in Replicator G 2. Removed "Build % Complete" changes as they are covered by other patches 3. Merged queryExtruderParameter and setExtruder16Bit Parameter into extruderCommand with a cmdType parameter --- firmware/src/Motherboard/EepromMap.cc | 3 + firmware/src/Motherboard/EepromMap.hh | 4 + firmware/src/shared/Menu.cc | 266 ++++++++++++++++++++++++-- firmware/src/shared/Menu.hh | 59 +++++- 4 files changed, 317 insertions(+), 15 deletions(-) diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index 4c7e8ee..642b821 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -30,6 +30,9 @@ void setDefaults() { eeprom_write_byte((uint8_t*)eeprom::AXIS_INVERSION,axis_invert); eeprom_write_byte((uint8_t*)eeprom::ENDSTOP_INVERSION,endstop_invert); eeprom_write_byte((uint8_t*)eeprom::MACHINE_NAME,0); // name is null + eeprom_write_byte((uint8_t*)eeprom::TOOL0_TEMP,220); + eeprom_write_byte((uint8_t*)eeprom::TOOL1_TEMP,220); + eeprom_write_byte((uint8_t*)eeprom::PLATFORM_TEMP,110); } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index 26b4f5e..1f4db49 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -50,6 +50,10 @@ const static uint16_t MACHINE_NAME = 0x0020; const static uint16_t AXIS_HOME_POSITIONS = 0x0060; +const static uint16_t TOOL0_TEMP = 0x0080; +const static uint16_t TOOL1_TEMP = 0x0081; +const static uint16_t PLATFORM_TEMP = 0x0082; + /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 0d8374f..e5a3b8d 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -17,6 +17,9 @@ #include #include #include "SDCard.hh" +#include "EepromMap.hh" +#include "Eeprom.hh" +#include #define HOST_PACKET_TIMEOUT_MS 20 @@ -25,8 +28,11 @@ #define HOST_TOOL_RESPONSE_TIMEOUT_MS 50 #define HOST_TOOL_RESPONSE_TIMEOUT_MICROS (1000L*HOST_TOOL_RESPONSE_TIMEOUT_MS) -/// Send a query packet to the extruder -bool queryExtruderParameter(uint8_t parameter, OutPacket& responsePacket) { +/// Send a packet to the extruder. If cmdType == EXTDR_CMD_SET, then "val" should +/// contain the value to be written otherwise val is ignored. +/// responsePacket is filled with the returned value +bool extruderControl(uint8_t command, enum extruderCommandType cmdType, + OutPacket& responsePacket, uint16_t val) { Timeout acquire_lock_timeout; acquire_lock_timeout.start(HOST_TOOL_RESPONSE_TIMEOUT_MS); @@ -43,7 +49,8 @@ bool queryExtruderParameter(uint8_t parameter, OutPacket& responsePacket) { // Fill the query packet. The first byte is the toolhead index, and the // second is the out.append8(0); - out.append8(parameter); + out.append8(command); + if ( cmdType == EXTDR_CMD_SET ) out.append16(val); // Timeouts are handled inside the toolslice code; there's no need // to check for timeouts on this loop. @@ -566,7 +573,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { switch (updatePhase) { case 0: lcd.setCursor(6,2); - if (queryExtruderParameter(SLAVE_CMD_GET_TEMP, responsePacket)) { + if (extruderControl(SLAVE_CMD_GET_TEMP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); lcd.writeInt(data,3); } else { @@ -576,7 +583,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { case 1: lcd.setCursor(10,2); - if (queryExtruderParameter(SLAVE_CMD_GET_SP, responsePacket)) { + if (extruderControl(SLAVE_CMD_GET_SP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); lcd.writeInt(data,3); } else { @@ -586,7 +593,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { case 2: lcd.setCursor(6,3); - if (queryExtruderParameter(SLAVE_CMD_GET_PLATFORM_TEMP, responsePacket)) { + if (extruderControl(SLAVE_CMD_GET_PLATFORM_TEMP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); lcd.writeInt(data,3); } else { @@ -596,7 +603,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { case 3: lcd.setCursor(10,3); - if (queryExtruderParameter(SLAVE_CMD_GET_PLATFORM_SP, responsePacket)) { + if (extruderControl(SLAVE_CMD_GET_PLATFORM_SP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); lcd.writeInt(data,3); } else { @@ -659,7 +666,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { //Set extruderStartSeconds to when the extruder starts extruding, so we can //get an accurate TimeLeft: if ( extruderStartSeconds == 0.0 ) { - if (queryExtruderParameter(SLAVE_CMD_GET_MOTOR_1_PWM, responsePacket)) { + if (extruderControl(SLAVE_CMD_GET_MOTOR_1_PWM, EXTDR_CMD_GET, responsePacket, 0)) { uint8_t pwm = responsePacket.read8(1); if ( pwm ) extruderStartSeconds = Motherboard::getBoard().getCurrentSeconds(); } @@ -727,7 +734,7 @@ void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { //Display the extruder version OutPacket responsePacket; - if (queryExtruderParameter(SLAVE_CMD_VERSION, responsePacket)) { + if (extruderControl(SLAVE_CMD_VERSION, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t extruderVersion = responsePacket.read16(1); lcd.setCursor(13, 3); @@ -877,14 +884,15 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 6; + itemCount = 7; reset(); } void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { static PROGMEM prog_uchar monitor[] = "Monitor Mode"; static PROGMEM prog_uchar build[] = "Build from SD"; - static PROGMEM prog_uchar jog[] = "Jog Mode"; + static PROGMEM prog_uchar jog[] = "Jog Mode"; + static PROGMEM prog_uchar preheat[] = "Preheat"; static PROGMEM prog_uchar versions[] = "Version"; static PROGMEM prog_uchar snake[] = "Snake Game"; @@ -899,12 +907,15 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(jog); break; case 3: - // blank + lcd.writeFromPgmspace(preheat); break; case 4: - lcd.writeFromPgmspace(versions); + // blank break; case 5: + lcd.writeFromPgmspace(versions); + break; + case 6: lcd.writeFromPgmspace(snake); break; } @@ -924,11 +935,19 @@ void MainMenu::handleSelect(uint8_t index) { // Show build from SD screen interface::pushScreen(&jogger); break; + case 3: + // Show preheat menu + interface::pushScreen(&preheatMenu); + preheatMenu.fetchTargetTemps(); + break; case 4: + // blank + break; + case 5: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 5: + case 6: // Show build from SD screen interface::pushScreen(&snake); break; @@ -1047,4 +1066,223 @@ void SDMenu::handleSelect(uint8_t index) { } } + +void Tool0TempSetScreen::reset() { + value = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220);; +} + +void Tool0TempSetScreen::update(LiquidCrystal& lcd, bool forceRedraw) { + static PROGMEM prog_uchar message1[] = "Tool0 Targ Temp:"; + static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + + // Redraw tool info + lcd.setCursor(0,1); + lcd.writeInt(value,3); +} + +void Tool0TempSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + case ButtonArray::OK: + eeprom_write_byte((uint8_t*)eeprom::TOOL0_TEMP,value); + interface::popScreen(); + break; + case ButtonArray::ZPLUS: + // increment more + if (value <= 250) { + value += 5; + } + break; + case ButtonArray::ZMINUS: + // decrement more + if (value >= 5) { + value -= 5; + } + break; + case ButtonArray::YPLUS: + // increment less + if (value <= 254) { + value += 1; + } + break; + case ButtonArray::YMINUS: + // decrement less + if (value >= 1) { + value -= 1; + } + break; + + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } +} + + +void PlatformTempSetScreen::reset() { + value = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110);; +} + +void PlatformTempSetScreen::update(LiquidCrystal& lcd, bool forceRedraw) { + static PROGMEM prog_uchar message1[] = "Bed Target Temp:"; + static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + + // Redraw tool info + lcd.setCursor(0,1); + lcd.writeInt(value,3); +} + +void PlatformTempSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + case ButtonArray::OK: + eeprom_write_byte((uint8_t*)eeprom::PLATFORM_TEMP,value); + interface::popScreen(); + break; + case ButtonArray::ZPLUS: + // increment more + if (value <= 250) { + value += 5; + } + break; + case ButtonArray::ZMINUS: + // decrement more + if (value >= 5) { + value -= 5; + } + break; + case ButtonArray::YPLUS: + // increment less + if (value <= 254) { + value += 1; + } + break; + case ButtonArray::YMINUS: + // decrement less + if (value >= 1) { + value -= 1; + } + break; + + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } +} + + +PreheatMenu::PreheatMenu() { + itemCount = 4; + reset(); +} + +void PreheatMenu::fetchTargetTemps() { + OutPacket responsePacket; + if (extruderControl(SLAVE_CMD_GET_SP, EXTDR_CMD_GET, responsePacket, 0)) { + tool0Temp = responsePacket.read16(1); + } + if (extruderControl(SLAVE_CMD_GET_PLATFORM_SP, EXTDR_CMD_GET, responsePacket, 0)) { + platformTemp = responsePacket.read16(1); + } +} + +void PreheatMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + static PROGMEM prog_uchar heat[] = "Heat "; + static PROGMEM prog_uchar cool[] = "Cool "; + static PROGMEM prog_uchar tool0[] = "Tool0"; + static PROGMEM prog_uchar platform[] = "Bed"; + static PROGMEM prog_uchar tool0set[] = "Set Tool0 Temp"; + static PROGMEM prog_uchar platset[] = "Set Bed Temp"; + + switch (index) { + case 0: + fetchTargetTemps(); + if (tool0Temp > 0) { + lcd.writeFromPgmspace(cool); + } else { + lcd.writeFromPgmspace(heat); + } + lcd.writeFromPgmspace(tool0); + break; + case 1: + if (platformTemp > 0) { + lcd.writeFromPgmspace(cool); + } else { + lcd.writeFromPgmspace(heat); + } + lcd.writeFromPgmspace(platform); + break; + case 2: + lcd.writeFromPgmspace(tool0set); + break; + case 3: + lcd.writeFromPgmspace(platset); + break; + } +} + +void PreheatMenu::handleSelect(uint8_t index) { + OutPacket responsePacket; + switch (index) { + case 0: + // Toggle Extruder heater on/off + if (tool0Temp > 0) { + extruderControl(SLAVE_CMD_SET_TEMP, EXTDR_CMD_SET, responsePacket, 0); + } else { + uint8_t value = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220); + extruderControl(SLAVE_CMD_SET_TEMP, EXTDR_CMD_SET, responsePacket, (uint16_t)value); + } + fetchTargetTemps(); + lastDrawIndex = 255; // forces redraw. + break; + case 1: + // Toggle Platform heater on/off + if (platformTemp > 0) { + extruderControl(SLAVE_CMD_SET_PLATFORM_TEMP, EXTDR_CMD_SET, responsePacket, 0); + } else { + uint8_t value = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110); + extruderControl(SLAVE_CMD_SET_PLATFORM_TEMP, EXTDR_CMD_SET, responsePacket, value); + } + fetchTargetTemps(); + lastDrawIndex = 255; // forces redraw. + break; + case 2: + // Show Extruder Temperature Setting Screen + interface::pushScreen(&tool0TempSetScreen); + break; + case 3: + // Show Platform Temperature Setting Screen + interface::pushScreen(&platTempSetScreen); + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index c75db4b..81dcb9a 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -5,6 +5,11 @@ #include "ButtonArray.hh" #include "LiquidCrystal.hh" +enum extruderCommandType { + EXTDR_CMD_GET, + EXTDR_CMD_SET +}; + /// The screen class defines a standard interface for anything that should /// be displayed on the LCD. class Screen { @@ -43,7 +48,7 @@ public: /// automatically. class Menu: public Screen { public: - micros_t getUpdateRate() {return 500L * 1000L;} + virtual micros_t getUpdateRate() {return 500L * 1000L;} void update(LiquidCrystal& lcd, bool forceRedraw); @@ -224,6 +229,57 @@ public: }; +class Tool0TempSetScreen: public Screen { +private: + uint8_t value; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + + +class PlatformTempSetScreen: public Screen { +private: + uint8_t value; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + + +class PreheatMenu: public Menu { +public: + PreheatMenu(); + + void fetchTargetTemps(); + +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); + +private: + uint16_t tool0Temp; + uint16_t platformTemp; + + /// Static instances of our menus + Tool0TempSetScreen tool0TempSetScreen; + PlatformTempSetScreen platTempSetScreen; +}; + + class MainMenu: public Menu { public: MainMenu(); @@ -238,6 +294,7 @@ private: MonitorMode monitorMode; SDMenu sdMenu; JogMode jogger; + PreheatMenu preheatMenu; VersionMode versionMode; SnakeMode snake; }; From 2531e303dd43f0ca69221fdf9deedc245b13fb29 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Sat, 24 Dec 2011 11:40:14 -0700 Subject: [PATCH 06/61] Changes to commit 881d776 by RonGarrison. This commit is meant as a replacement. Credit for the original code goes to RonGarrison. 1. Used Extruder Controller changes only 2. Change enum time_t to enum extruderSeconds to avoid confusion with time.h 3. Removed changeTemp as it's covered by revarbat 4. Improved UI and Code optimization 5. Added stop button 6. Allow time extruded to change midway 7. Corrected foward / back direction 8. Added temperature safety check before extruding 9. Added RPM Extruder Speed (also remembered, value stored in eeprom) 10. Extrude duration is now remembered in eeprom 11. Word "Mode" dropped from Menu for consistency 12. Added more extrude duration options --- firmware/src/Motherboard/EepromMap.cc | 2 + firmware/src/Motherboard/EepromMap.hh | 2 + firmware/src/shared/LiquidCrystal.cc | 61 ++++++ firmware/src/shared/LiquidCrystal.hh | 2 + firmware/src/shared/Menu.cc | 300 +++++++++++++++++++++++++- firmware/src/shared/Menu.hh | 60 ++++++ 6 files changed, 419 insertions(+), 8 deletions(-) diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index 642b821..acebe3f 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -33,6 +33,8 @@ void setDefaults() { eeprom_write_byte((uint8_t*)eeprom::TOOL0_TEMP,220); eeprom_write_byte((uint8_t*)eeprom::TOOL1_TEMP,220); eeprom_write_byte((uint8_t*)eeprom::PLATFORM_TEMP,110); + eeprom_write_byte((uint8_t*)eeprom::EXTRUDE_DURATION,1); + eeprom_write_byte((uint8_t*)eeprom::EXTRUDE_RPM,19); } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index 1f4db49..f7b035e 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -53,6 +53,8 @@ const static uint16_t AXIS_HOME_POSITIONS = 0x0060; const static uint16_t TOOL0_TEMP = 0x0080; const static uint16_t TOOL1_TEMP = 0x0081; const static uint16_t PLATFORM_TEMP = 0x0082; +const static uint16_t EXTRUDE_DURATION= 0x0083; +const static uint16_t EXTRUDE_RPM = 0x0084; /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/shared/LiquidCrystal.cc b/firmware/src/shared/LiquidCrystal.cc index f46f716..0513e70 100755 --- a/firmware/src/shared/LiquidCrystal.cc +++ b/firmware/src/shared/LiquidCrystal.cc @@ -281,6 +281,67 @@ void LiquidCrystal::writeInt(uint16_t value, uint8_t digits) { } +//From: http://www.arduino.cc/playground/Code/PrintFloats +//tim [at] growdown [dot] com Ammended to write a float to lcd + +void LiquidCrystal::writeFloat(float value, uint8_t decimalPlaces) { + // this is used to cast digits + int digit; + float tens = 0.1; + int tenscount = 0; + int i; + float tempfloat = value; + + // make sure we round properly. this could use pow from , but doesn't seem worth the import + // if this rounding step isn't here, the value 54.321 prints as 54.3209 + + // calculate rounding term d: 0.5/pow(10,decimalPlaces) + float d = 0.5; + if (value < 0) d *= -1.0; + + // divide by ten for each decimal place + for (i = 0; i < decimalPlaces; i++) d/= 10.0; + + // this small addition, combined with truncation will round our values properly + tempfloat += d; + + // first get value tens to be the large power of ten less than value + // tenscount isn't necessary but it would be useful if you wanted to know after this how many chars the number will take + + if (value < 0) tempfloat *= -1.0; + while ((tens * 10.0) <= tempfloat) { + tens *= 10.0; + tenscount += 1; + } + + // write out the negative if needed + if (value < 0) write('-'); + + if (tenscount == 0) write('0'); + + for (i=0; i< tenscount; i++) { + digit = (int) (tempfloat/tens); + write(digit + '0'); + tempfloat = tempfloat - ((float)digit * tens); + tens /= 10.0; + } + + // if no decimalPlaces after decimal, stop now and return + if (decimalPlaces <= 0) return; + + // otherwise, write the point and continue on + write('.'); + + // now write out each decimal place by shifting digits one by one into the ones place and writing the truncated value + for (i = 0; i < decimalPlaces; i++) { + tempfloat *= 10.0; + digit = (int) tempfloat; + write(digit+'0'); + // once written, subtract off that digit + tempfloat = tempfloat - (float) digit; + } +} + void LiquidCrystal::writeString(char message[]) { char* letter = message; while (*letter != 0) { diff --git a/firmware/src/shared/LiquidCrystal.hh b/firmware/src/shared/LiquidCrystal.hh index 7b70f0e..081f73d 100755 --- a/firmware/src/shared/LiquidCrystal.hh +++ b/firmware/src/shared/LiquidCrystal.hh @@ -87,6 +87,8 @@ public: /** Added by MakerBot Industries to support storing strings in flash **/ void writeInt(uint16_t value, uint8_t digits); + void writeFloat(float value, uint8_t decimalPlaces); + void writeString(char message[]); void writeFromPgmspace(const prog_uchar message[]); diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index e5a3b8d..745dbc0 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -28,6 +28,8 @@ #define HOST_TOOL_RESPONSE_TIMEOUT_MS 50 #define HOST_TOOL_RESPONSE_TIMEOUT_MICROS (1000L*HOST_TOOL_RESPONSE_TIMEOUT_MS) +int16_t overrideExtrudeSeconds = 0; + /// Send a packet to the extruder. If cmdType == EXTDR_CMD_SET, then "val" should /// contain the value to be written otherwise val is ignored. /// responsePacket is filled with the returned value @@ -400,6 +402,286 @@ void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } +void ExtruderMode::reset() { + extrudeSeconds = (enum extrudeSeconds)eeprom::getEeprom8(eeprom::EXTRUDE_DURATION, 1); + updatePhase = 0; + timeChanged = false; + lastDirection = 1; + overrideExtrudeSeconds = 0; +} + +void ExtruderMode::update(LiquidCrystal& lcd, bool forceRedraw) { + static PROGMEM prog_uchar extrude1[] = "Extrude: "; + static PROGMEM prog_uchar extrude2[] = "(set rpm) Fwd"; + static PROGMEM prog_uchar extrude3[] = " (stop) (dur)"; + static PROGMEM prog_uchar extrude4[] = "---/---C Rev"; + static PROGMEM prog_uchar secs[] = "SECS"; + static PROGMEM prog_uchar blank[] = " "; + + if (overrideExtrudeSeconds) extrude(overrideExtrudeSeconds, true); + + if (forceRedraw) { + lcd.clear(); + lcd.setCursor(0,0); + lcd.writeFromPgmspace(extrude1); + + lcd.setCursor(0,1); + lcd.writeFromPgmspace(extrude2); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(extrude3); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(extrude4); + } + + if ((forceRedraw) || (timeChanged)) { + lcd.setCursor(9,0); + lcd.writeFromPgmspace(blank); + lcd.setCursor(9,0); + lcd.writeFloat((float)extrudeSeconds, 0); + lcd.writeFromPgmspace(secs); + timeChanged = false; + } + + OutPacket responsePacket; + Point position; + + // Redraw tool info + switch (updatePhase) { + case 0: + lcd.setCursor(0,3); + if (extruderControl(SLAVE_CMD_GET_TEMP, EXTDR_CMD_GET, responsePacket, 0)) { + uint16_t data = responsePacket.read16(1); + lcd.writeInt(data,3); + } else { + lcd.writeString("XXX"); + } + break; + + case 1: + lcd.setCursor(4,3); + if (extruderControl(SLAVE_CMD_GET_SP, EXTDR_CMD_GET, responsePacket, 0)) { + uint16_t data = responsePacket.read16(1); + lcd.writeInt(data,3); + } else { + lcd.writeString("XXX"); + } + break; + } + + updatePhase++; + if (updatePhase > 1) { + updatePhase = 0; + } +} + +void ExtruderMode::extrude(seconds_t seconds, bool overrideTempCheck) { + //Check we're hot enough + if ( ! overrideTempCheck ) + { + OutPacket responsePacket; + if (extruderControl(SLAVE_CMD_IS_TOOL_READY, EXTDR_CMD_GET, responsePacket, 0)) { + uint8_t data = responsePacket.read8(1); + + if ( ! data ) + { + overrideExtrudeSeconds = seconds; + interface::pushScreen(&extruderTooColdMenu); + return; + } + } + } + + Point position = steppers::getPosition(); + + float rpm = (float)eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19) / 10.0; + + //60 * 1000000 = # uS in a minute + //200 * 8 = 200 steps per revolution * 1/8 stepping + int32_t interval = (int32_t)(60L * 1000000L) / (int32_t)((float)(200 * 8) * rpm); + int16_t stepsPerSecond = (int16_t)((200.0 * 8.0 * rpm) / 60.0); + + if ( seconds == 0 ) steppers::abort(); + else { + position[3] += seconds * stepsPerSecond; + steppers::setTarget(position, interval); + } + + if (overrideTempCheck) overrideExtrudeSeconds = 0; +} + +void ExtruderMode::notifyButtonPressed(ButtonArray::ButtonName button) { + int16_t zReverse = -1; + + switch (button) { + case ButtonArray::OK: + switch(extrudeSeconds) { + case EXTRUDE_SECS_1S: + extrudeSeconds = EXTRUDE_SECS_2S; + break; + case EXTRUDE_SECS_2S: + extrudeSeconds = EXTRUDE_SECS_5S; + break; + case EXTRUDE_SECS_5S: + extrudeSeconds = EXTRUDE_SECS_10S; + break; + case EXTRUDE_SECS_10S: + extrudeSeconds = EXTRUDE_SECS_30S; + break; + case EXTRUDE_SECS_30S: + extrudeSeconds = EXTRUDE_SECS_60S; + break; + case EXTRUDE_SECS_60S: + extrudeSeconds = EXTRUDE_SECS_90S; + break; + case EXTRUDE_SECS_90S: + extrudeSeconds = EXTRUDE_SECS_120S; + break; + case EXTRUDE_SECS_120S: + extrudeSeconds = EXTRUDE_SECS_240S; + break; + case EXTRUDE_SECS_240S: + extrudeSeconds = EXTRUDE_SECS_1S; + break; + default: + extrudeSeconds = EXTRUDE_SECS_1S; + break; + } + + eeprom_write_byte((uint8_t *)eeprom::EXTRUDE_DURATION, (uint8_t)extrudeSeconds); + + //If we're already extruding, change the time running + if (steppers::isRunning()) + extrude((seconds_t)(zReverse * lastDirection * extrudeSeconds), false); + + timeChanged = true; + break; + case ButtonArray::YPLUS: + // Show Extruder RPM Setting Screen + interface::pushScreen(&extruderSetRpmScreen); + break; + case ButtonArray::ZERO: + case ButtonArray::YMINUS: + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + extrude((seconds_t)EXTRUDE_SECS_CANCEL, true); + break; + case ButtonArray::ZMINUS: + case ButtonArray::ZPLUS: + if ( button == ButtonArray::ZPLUS ) lastDirection = 1; + else lastDirection = -1; + + extrude((seconds_t)(zReverse * lastDirection * extrudeSeconds), false); + break; + case ButtonArray::CANCEL: + interface::popScreen(); + break; + } +} + + + +ExtruderTooColdMenu::ExtruderTooColdMenu() { + itemCount = 4; + reset(); +} + +void ExtruderTooColdMenu::resetState() { + itemIndex = 2; + firstItemIndex = 2; +} + +void ExtruderTooColdMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + static PROGMEM prog_uchar warning[] = "Tool0 too cold!"; + static PROGMEM prog_uchar cancel[] = "Cancel"; + static PROGMEM prog_uchar override[] = "Override"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(warning); + break; + case 1: + break; + case 2: + lcd.writeFromPgmspace(cancel); + break; + case 3: + lcd.writeFromPgmspace(override); + break; + } +} + +void ExtruderTooColdMenu::handleSelect(uint8_t index) { + switch (index) { + case 2: + // Cancel extrude + overrideExtrudeSeconds = 0; + interface::popScreen(); + break; + case 3: + // Override and extrude + interface::popScreen(); + break; + } +} + +void ExtruderSetRpmScreen::reset() { + rpm = eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19); +} + +void ExtruderSetRpmScreen::update(LiquidCrystal& lcd, bool forceRedraw) { + static PROGMEM prog_uchar message1[] = "Extruder RPM:"; + static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + static PROGMEM prog_uchar blank[] = " "; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(0,1); + lcd.writeFloat((float)rpm / 10.0, 1); + lcd.writeFromPgmspace(blank); +} + +void ExtruderSetRpmScreen::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + case ButtonArray::OK: + eeprom_write_byte((uint8_t *)eeprom::EXTRUDE_RPM, rpm); + interface::popScreen(); + break; + case ButtonArray::ZPLUS: + // increment more + if (rpm <= 250) rpm += 5; + break; + case ButtonArray::ZMINUS: + // decrement more + if (rpm >= 8) rpm -= 5; + break; + case ButtonArray::YPLUS: + // increment less + if (rpm <= 254) rpm += 1; + break; + case ButtonArray::YMINUS: + // decrement less + if (rpm >= 4) rpm -= 1; + break; + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } +} void SnakeMode::update(LiquidCrystal& lcd, bool forceRedraw) { static PROGMEM prog_uchar gameOver[] = "GAME OVER!"; @@ -889,12 +1171,13 @@ MainMenu::MainMenu() { } void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - static PROGMEM prog_uchar monitor[] = "Monitor Mode"; - static PROGMEM prog_uchar build[] = "Build from SD"; - static PROGMEM prog_uchar jog[] = "Jog Mode"; - static PROGMEM prog_uchar preheat[] = "Preheat"; - static PROGMEM prog_uchar versions[] = "Version"; - static PROGMEM prog_uchar snake[] = "Snake Game"; + static PROGMEM prog_uchar monitor[] = "Monitor"; + static PROGMEM prog_uchar build[] = "Build from SD"; + static PROGMEM prog_uchar jog[] = "Jog"; + static PROGMEM prog_uchar preheat[] = "Preheat"; + static PROGMEM prog_uchar extruder[] = "Extrude"; + static PROGMEM prog_uchar versions[] = "Version"; + static PROGMEM prog_uchar snake[] = "Snake Game"; switch (index) { case 0: @@ -910,7 +1193,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(preheat); break; case 4: - // blank + lcd.writeFromPgmspace(extruder); break; case 5: lcd.writeFromPgmspace(versions); @@ -941,7 +1224,8 @@ void MainMenu::handleSelect(uint8_t index) { preheatMenu.fetchTargetTemps(); break; case 4: - // blank + // Show extruder menu + interface::pushScreen(&extruderMenu); break; case 5: // Show build from SD screen diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 81dcb9a..2c73273 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -279,6 +279,65 @@ private: PlatformTempSetScreen platTempSetScreen; }; +class ExtruderTooColdMenu: public Menu { +public: + ExtruderTooColdMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + +class ExtruderSetRpmScreen: public Screen { +private: + uint8_t rpm; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + +class ExtruderMode: public Screen { +private: + enum extrudeSeconds { + EXTRUDE_SECS_CANCEL = 0, + EXTRUDE_SECS_1S = 1, + EXTRUDE_SECS_2S = 2, + EXTRUDE_SECS_5S = 5, + EXTRUDE_SECS_10S = 10, + EXTRUDE_SECS_30S = 30, + EXTRUDE_SECS_60S = 60, + EXTRUDE_SECS_90S = 90, + EXTRUDE_SECS_120S = 120, + EXTRUDE_SECS_240S = 240, + }; + + enum extrudeSeconds extrudeSeconds; + bool timeChanged; + int16_t lastDirection; + ExtruderTooColdMenu extruderTooColdMenu; + ExtruderSetRpmScreen extruderSetRpmScreen; + + uint8_t updatePhase; + + void extrude(seconds_t steps, bool overrideTempCheck); + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; class MainMenu: public Menu { public: @@ -295,6 +354,7 @@ private: SDMenu sdMenu; JogMode jogger; PreheatMenu preheatMenu; + ExtruderMode extruderMenu; VersionMode versionMode; SnakeMode snake; }; From b79196bef2f91236a04ffd50a4b4847ea4c03f3d Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Thu, 29 Dec 2011 16:47:23 -0700 Subject: [PATCH 07/61] Changed static PROGMEM to const static PROGMEM so firmware will compile on newer avr-gcc --- firmware/src/Motherboard/Host.cc | 2 +- firmware/src/shared/Menu.cc | 114 ++++++++++++------------- firmware/src/shared/ThermistorTable.cc | 2 +- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/firmware/src/Motherboard/Host.cc b/firmware/src/Motherboard/Host.cc index 1a35c69..7b6a962 100644 --- a/firmware/src/Motherboard/Host.cc +++ b/firmware/src/Motherboard/Host.cc @@ -540,7 +540,7 @@ char* getMachineName() { } // If it's still zero, load in a default. - static PROGMEM prog_uchar defaultMachineName[] = "Thing-O-Matic"; + const static PROGMEM prog_uchar defaultMachineName[] = "Thing-O-Matic"; if (machineName[0] == 0) { for(uint8_t i = 0; i < 14; i++) { diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 745dbc0..93f8334 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -237,10 +237,10 @@ int appendUint8(char *buf, uint8_t buflen, uint8_t val) void SplashScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar splash1[] = " "; - static PROGMEM prog_uchar splash2[] = " Thing-O-Matic "; - static PROGMEM prog_uchar splash3[] = " --------- "; - static PROGMEM prog_uchar splash4[] = " "; + const static PROGMEM prog_uchar splash1[] = " "; + const static PROGMEM prog_uchar splash2[] = " Thing-O-Matic "; + const static PROGMEM prog_uchar splash3[] = " --------- "; + const static PROGMEM prog_uchar splash4[] = " "; if (forceRedraw) { @@ -276,14 +276,14 @@ void JogMode::reset() { } void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar jog1[] = "Jog mode: "; - static PROGMEM prog_uchar jog2[] = " Y+ Z+"; - static PROGMEM prog_uchar jog3[] = "X- X+ (mode)"; - static PROGMEM prog_uchar jog4[] = " Y- Z-"; + const static PROGMEM prog_uchar jog1[] = "Jog mode: "; + const static PROGMEM prog_uchar jog2[] = " Y+ Z+"; + const static PROGMEM prog_uchar jog3[] = "X- X+ (mode)"; + const static PROGMEM prog_uchar jog4[] = " Y- Z-"; - static PROGMEM prog_uchar distanceShort[] = "SHORT"; - static PROGMEM prog_uchar distanceLong[] = "LONG"; - static PROGMEM prog_uchar distanceCont[] = "CONT"; + const static PROGMEM prog_uchar distanceShort[] = "SHORT"; + const static PROGMEM prog_uchar distanceLong[] = "LONG"; + const static PROGMEM prog_uchar distanceCont[] = "CONT"; if (forceRedraw || distanceChanged) { lcd.clear(); @@ -411,12 +411,12 @@ void ExtruderMode::reset() { } void ExtruderMode::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar extrude1[] = "Extrude: "; - static PROGMEM prog_uchar extrude2[] = "(set rpm) Fwd"; - static PROGMEM prog_uchar extrude3[] = " (stop) (dur)"; - static PROGMEM prog_uchar extrude4[] = "---/---C Rev"; - static PROGMEM prog_uchar secs[] = "SECS"; - static PROGMEM prog_uchar blank[] = " "; + const static PROGMEM prog_uchar extrude1[] = "Extrude: "; + const static PROGMEM prog_uchar extrude2[] = "(set rpm) Fwd"; + const static PROGMEM prog_uchar extrude3[] = " (stop) (dur)"; + const static PROGMEM prog_uchar extrude4[] = "---/---C Rev"; + const static PROGMEM prog_uchar secs[] = "SECS"; + const static PROGMEM prog_uchar blank[] = " "; if (overrideExtrudeSeconds) extrude(overrideExtrudeSeconds, true); @@ -593,9 +593,9 @@ void ExtruderTooColdMenu::resetState() { } void ExtruderTooColdMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - static PROGMEM prog_uchar warning[] = "Tool0 too cold!"; - static PROGMEM prog_uchar cancel[] = "Cancel"; - static PROGMEM prog_uchar override[] = "Override"; + const static PROGMEM prog_uchar warning[] = "Tool0 too cold!"; + const static PROGMEM prog_uchar cancel[] = "Cancel"; + const static PROGMEM prog_uchar override[] = "Override"; switch (index) { case 0: @@ -631,9 +631,9 @@ void ExtruderSetRpmScreen::reset() { } void ExtruderSetRpmScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar message1[] = "Extruder RPM:"; - static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; - static PROGMEM prog_uchar blank[] = " "; + const static PROGMEM prog_uchar message1[] = "Extruder RPM:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; if (forceRedraw) { lcd.clear(); @@ -684,7 +684,7 @@ void ExtruderSetRpmScreen::notifyButtonPressed(ButtonArray::ButtonName button) { } void SnakeMode::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar gameOver[] = "GAME OVER!"; + const static PROGMEM prog_uchar gameOver[] = "GAME OVER!"; // If we are dead, restart the game. if (!snakeAlive) { @@ -813,14 +813,14 @@ void MonitorMode::reset() { } void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar extruder_temp[] = "Tool: ---/---C"; - static PROGMEM prog_uchar platform_temp[] = "Bed: ---/---C"; - static PROGMEM prog_uchar elapsed_time[] = "Elapsed: 0h00m"; - static PROGMEM prog_uchar completed_percent[] = "Completed: 0% "; - static PROGMEM prog_uchar time_left[] = "TimeLeft: 0h00m"; - static PROGMEM prog_uchar time_left_calc[] = " calc.."; - static PROGMEM prog_uchar time_left_1min[] = " <1min"; - static PROGMEM prog_uchar time_left_none[] = " none"; + const static PROGMEM prog_uchar extruder_temp[] = "Tool: ---/---C"; + const static PROGMEM prog_uchar platform_temp[] = "Bed: ---/---C"; + const static PROGMEM prog_uchar elapsed_time[] = "Elapsed: 0h00m"; + const static PROGMEM prog_uchar completed_percent[] = "Completed: 0% "; + const static PROGMEM prog_uchar time_left[] = "TimeLeft: 0h00m"; + const static PROGMEM prog_uchar time_left_calc[] = " calc.."; + const static PROGMEM prog_uchar time_left_1min[] = " <1min"; + const static PROGMEM prog_uchar time_left_none[] = " none"; char buf[17]; if (forceRedraw) { @@ -986,10 +986,10 @@ void VersionMode::reset() { } void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar version1[] = "Firmware Version"; - static PROGMEM prog_uchar version2[] = "----------------"; - static PROGMEM prog_uchar version3[] = "Motherboard: _._"; - static PROGMEM prog_uchar version4[] = " Extruder: _._"; + const static PROGMEM prog_uchar version1[] = "Firmware Version"; + const static PROGMEM prog_uchar version2[] = "----------------"; + const static PROGMEM prog_uchar version3[] = "Motherboard: _._"; + const static PROGMEM prog_uchar version4[] = " Extruder: _._"; if (forceRedraw) { lcd.clear(); @@ -1037,7 +1037,7 @@ void VersionMode::notifyButtonPressed(ButtonArray::ButtonName button) { } void Menu::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar blankLine[] = " "; + const static PROGMEM prog_uchar blankLine[] = " "; // Do we need to redraw the whole menu? if ((itemIndex/LCD_SCREEN_HEIGHT) != (lastDrawIndex/LCD_SCREEN_HEIGHT) @@ -1130,9 +1130,9 @@ void CancelBuildMenu::resetState() { } void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - static PROGMEM prog_uchar cancel[] = "Cancel Build?"; - static PROGMEM prog_uchar yes[] = "Yes"; - static PROGMEM prog_uchar no[] = "No"; + const static PROGMEM prog_uchar cancel[] = "Cancel Build?"; + const static PROGMEM prog_uchar yes[] = "Yes"; + const static PROGMEM prog_uchar no[] = "No"; switch (index) { case 0: @@ -1171,13 +1171,13 @@ MainMenu::MainMenu() { } void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - static PROGMEM prog_uchar monitor[] = "Monitor"; - static PROGMEM prog_uchar build[] = "Build from SD"; - static PROGMEM prog_uchar jog[] = "Jog"; - static PROGMEM prog_uchar preheat[] = "Preheat"; - static PROGMEM prog_uchar extruder[] = "Extrude"; - static PROGMEM prog_uchar versions[] = "Version"; - static PROGMEM prog_uchar snake[] = "Snake Game"; + const static PROGMEM prog_uchar monitor[] = "Monitor"; + const static PROGMEM prog_uchar build[] = "Build from SD"; + const static PROGMEM prog_uchar jog[] = "Jog"; + const static PROGMEM prog_uchar preheat[] = "Preheat"; + const static PROGMEM prog_uchar extruder[] = "Extrude"; + const static PROGMEM prog_uchar versions[] = "Version"; + const static PROGMEM prog_uchar snake[] = "Snake Game"; switch (index) { case 0: @@ -1356,8 +1356,8 @@ void Tool0TempSetScreen::reset() { } void Tool0TempSetScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar message1[] = "Tool0 Targ Temp:"; - static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar message1[] = "Tool0 Targ Temp:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; if (forceRedraw) { lcd.clear(); @@ -1422,8 +1422,8 @@ void PlatformTempSetScreen::reset() { } void PlatformTempSetScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - static PROGMEM prog_uchar message1[] = "Bed Target Temp:"; - static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar message1[] = "Bed Target Temp:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; if (forceRedraw) { lcd.clear(); @@ -1499,12 +1499,12 @@ void PreheatMenu::fetchTargetTemps() { } void PreheatMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - static PROGMEM prog_uchar heat[] = "Heat "; - static PROGMEM prog_uchar cool[] = "Cool "; - static PROGMEM prog_uchar tool0[] = "Tool0"; - static PROGMEM prog_uchar platform[] = "Bed"; - static PROGMEM prog_uchar tool0set[] = "Set Tool0 Temp"; - static PROGMEM prog_uchar platset[] = "Set Bed Temp"; + const static PROGMEM prog_uchar heat[] = "Heat "; + const static PROGMEM prog_uchar cool[] = "Cool "; + const static PROGMEM prog_uchar tool0[] = "Tool0"; + const static PROGMEM prog_uchar platform[] = "Bed"; + const static PROGMEM prog_uchar tool0set[] = "Set Tool0 Temp"; + const static PROGMEM prog_uchar platset[] = "Set Bed Temp"; switch (index) { case 0: diff --git a/firmware/src/shared/ThermistorTable.cc b/firmware/src/shared/ThermistorTable.cc index 3fbc634..a13b851 100644 --- a/firmware/src/shared/ThermistorTable.cc +++ b/firmware/src/shared/ThermistorTable.cc @@ -42,7 +42,7 @@ const static int NUMTEMPS = 20; // max adc: 1023 typedef int16_t TempTable[NUMTEMPS][2]; -TempTable default_table PROGMEM = { +const TempTable default_table PROGMEM = { {1, 841}, {54, 255}, {107, 209}, From f788d790b462802ecfab1fd14dbf3634bf972a0c Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Thu, 29 Dec 2011 17:12:26 -0700 Subject: [PATCH 08/61] Changes to commit 97db22a by RonGarrison (ESTOP support for Gen 4 Motherboard). This commit is meant as a replacement. Credit for the original code goes to RonGarrison. Fixed problem with it not compiling for target rrmbv12 --- firmware/src/Motherboard/EepromMap.hh | 8 ++++++ firmware/src/Motherboard/Errors.hh | 2 +- firmware/src/Motherboard/Main.cc | 26 +++++++++++++++++++ .../Motherboard/boards/mb24/Configuration.hh | 4 +++ .../Motherboard/boards/mb24/Motherboard.cc | 6 +++++ 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index f7b035e..4ccec87 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -49,6 +49,14 @@ const static uint16_t MACHINE_NAME = 0x0020; /// Default locations for the axis: 5 x 32 bit = 20 bytes const static uint16_t AXIS_HOME_POSITIONS = 0x0060; +// Estop configuration byte: 1 byte. +const static uint16_t ESTOP_CONFIGURATION = 0x0074; + +enum { + ESTOP_CONF_NONE = 0x0, + ESTOP_CONF_ACTIVE_HIGH = 0x1, + ESTOP_CONF_ACTIVE_LOW = 0x2 +}; const static uint16_t TOOL0_TEMP = 0x0080; const static uint16_t TOOL1_TEMP = 0x0081; diff --git a/firmware/src/Motherboard/Errors.hh b/firmware/src/Motherboard/Errors.hh index 5207c87..c47736f 100644 --- a/firmware/src/Motherboard/Errors.hh +++ b/firmware/src/Motherboard/Errors.hh @@ -29,7 +29,7 @@ #define ERR_HOST_PACKET_TIMEOUT 4 #define ERR_HOST_PACKET_MISC 5 #define ERR_WDT_TIMEOUT 6 - +#define ERR_ESTOP 7 #define ERR_HOST_TRUNCATED_CMD 8 #endif /* ERRORS_HH_ */ diff --git a/firmware/src/Motherboard/Main.cc b/firmware/src/Motherboard/Main.cc index 147c157..a4d3f4b 100644 --- a/firmware/src/Motherboard/Main.cc +++ b/firmware/src/Motherboard/Main.cc @@ -28,6 +28,7 @@ #include "SDCard.hh" #include "Eeprom.hh" #include "EepromMap.hh" +#include "Errors.hh" void reset(bool hard_reset) { ATOMIC_BLOCK(ATOMIC_FORCEON) { @@ -37,6 +38,16 @@ void reset(bool hard_reset) { command::reset(); eeprom::init(); board.reset(); +#ifdef HAS_ESTOP + const uint8_t estop_conf = eeprom::getEeprom8(eeprom::ESTOP_CONFIGURATION, 0); + if (estop_conf == eeprom::ESTOP_CONF_ACTIVE_HIGH) { + ESTOP_ENABLE_RISING_INT; + } else if (estop_conf == eeprom::ESTOP_CONF_ACTIVE_LOW) { + ESTOP_ENABLE_FALLING_INT; + } +#endif + + sei(); // If we've just come from a hard reset, wait for 2.5 seconds before // trying to ping an extruder. This gives the extruder time to boot @@ -72,3 +83,18 @@ int main() { } return 0; } + +#ifdef HAS_ESTOP +ISR(ESTOP_vect, ISR_NOBLOCK) { + // Emergency stop triggered; reset everything and kill the interface to RepG + INTERFACE_BAR_PIN.setValue(true); + INTERFACE_BAR_PIN.setDirection(true); + tool::reset(); + steppers::abort(); + command::reset(); + UART::getHostUART().enable(false); + Motherboard::getBoard().indicateError(ERR_ESTOP); + +} +#endif + diff --git a/firmware/src/Motherboard/boards/mb24/Configuration.hh b/firmware/src/Motherboard/boards/mb24/Configuration.hh index f47eedb..82c6d89 100644 --- a/firmware/src/Motherboard/boards/mb24/Configuration.hh +++ b/firmware/src/Motherboard/boards/mb24/Configuration.hh @@ -73,6 +73,10 @@ #define HAS_ESTOP 1 // The pin connected to the emergency stop #define ESTOP_PIN Pin(PortE,4) +// Macros for enabling interrupts on the the pin. In this case, INT4. +#define ESTOP_ENABLE_RISING_INT { EICRB = 0x03; EIMSK |= 0x10; } +#define ESTOP_ENABLE_FALLING_INT { EICRB = 0x02; EIMSK |= 0x10; } +#define ESTOP_vect INT4_vect // --- Axis configuration --- diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index fbcc1af..f868fec 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -132,6 +132,12 @@ void Motherboard::reset() { // Configure the debug pin. DEBUG_PIN.setDirection(true); +#if HAS_ESTOP + // Configure the estop pin direction. + ESTOP_PIN.setDirection(false); +#endif + + // Check if the interface board is attached hasInterfaceBoard = interface::isConnected(); From aef0b130af80dd91522c77dd019e71538982e8b8 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Tue, 3 Jan 2012 16:01:56 -0700 Subject: [PATCH 09/61] Added BlinkM Mood Light Support for V2.4 Motherboard. This uses the BlinkM MaxM and RGB LED Strip to create a mood light effect. Mood light can be controlled via Gen 4 Interface or via GCode from ReplicatorG. Communication is via Software I2C over Arduino Mega ports A8 and A9. 4.7K pullups are required on these ports. --- firmware/src/Motherboard/Command.cc | 259 +++++++++++++-- firmware/src/Motherboard/Command.hh | 2 + firmware/src/Motherboard/EepromMap.cc | 4 + firmware/src/Motherboard/EepromMap.hh | 14 +- firmware/src/Motherboard/ExtruderControl.cc | 73 +++++ firmware/src/Motherboard/ExtruderControl.hh | 37 +++ .../Motherboard/boards/mb24/Configuration.hh | 7 + .../Motherboard/boards/mb24/Motherboard.cc | 42 ++- .../Motherboard/boards/mb24/Motherboard.hh | 8 + firmware/src/shared/BlinkMMaxM.cc | 280 ++++++++++++++++ firmware/src/shared/BlinkMMaxM.hh | 81 +++++ firmware/src/shared/Commands.hh | 3 + firmware/src/shared/Interface.cc | 4 + firmware/src/shared/Interface.hh | 2 + firmware/src/shared/InterfaceBoard.cc | 7 +- firmware/src/shared/InterfaceBoard.hh | 5 +- firmware/src/shared/Menu.cc | 260 ++++++++++++--- firmware/src/shared/Menu.hh | 42 ++- firmware/src/shared/MoodLightController.cc | 301 ++++++++++++++++++ firmware/src/shared/MoodLightController.hh | 60 ++++ firmware/src/shared/SoftwareI2C.cc | 168 ++++++++++ firmware/src/shared/SoftwareI2C.hh | 52 +++ 22 files changed, 1607 insertions(+), 104 deletions(-) create mode 100755 firmware/src/Motherboard/ExtruderControl.cc create mode 100755 firmware/src/Motherboard/ExtruderControl.hh create mode 100755 firmware/src/shared/BlinkMMaxM.cc create mode 100755 firmware/src/shared/BlinkMMaxM.hh create mode 100755 firmware/src/shared/MoodLightController.cc create mode 100755 firmware/src/shared/MoodLightController.hh create mode 100755 firmware/src/shared/SoftwareI2C.cc create mode 100755 firmware/src/shared/SoftwareI2C.hh diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index 91a6865..a3311a8 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -26,6 +26,7 @@ #include #include "EepromMap.hh" #include "SDCard.hh" +#include "ExtruderControl.hh" namespace command { @@ -37,6 +38,10 @@ bool outstanding_tool_command = false; bool paused = false; +uint16_t statusDivisor = 0; +uint32_t recentCommandClock = 0; +uint32_t recentCommandTime = 0; + uint16_t getRemainingCapacity() { uint16_t sz; ATOMIC_BLOCK(ATOMIC_FORCEON) { @@ -111,8 +116,67 @@ void reset() { mode = READY; } + +//Executes a slave command +//Returns true if everything is okay, otherwise false in case of error +//The result from the command is stored in result + +bool querySlaveCmd(uint8_t cmd, uint8_t *result) { + bool ret = false; + + if (tool::getLock()) { + OutPacket& out = tool::getOutPacket(); + InPacket& in = tool::getInPacket(); + out.reset(); + out.append8(tool::getCurrentToolheadIndex()); + out.append8(SLAVE_CMD_GET_TOOL_STATUS); + tool::startTransaction(); + + // WHILE: bounded by timeout in runToolSlice + while (!tool::isTransactionDone()) { + tool::runToolSlice(); + } + if (!in.hasError()) { + *result = in.read8(1); + ret = true; + } + else ret = false; + tool::releaseLock(); + } + + return ret; +} + + +bool isToolReady() { + uint8_t result; + + if (querySlaveCmd(SLAVE_CMD_GET_TOOL_STATUS, &result)) { + if (result & 0x01) return true; + } + + return false; +} + + +bool isPlatformReady() { + uint8_t result; + + if (querySlaveCmd(SLAVE_CMD_IS_PLATFORM_READY, &result)) { + if (result != 0) return true; + } + return false; +} + + // A fast slice for processing commands and refilling the stepper queue, etc. void runCommandSlice() { + recentCommandClock ++; + +#ifdef HAS_MOOD_LIGHT + updateMoodStatus(); +#endif + if (sdcard::isPlaying()) { while (command_buffer.getRemainingCapacity() > 0 && sdcard::playbackHasNext()) { command_buffer.push(sdcard::playbackNext()); @@ -120,6 +184,7 @@ void runCommandSlice() { } if (paused) { return; } if (mode == HOMING) { + recentCommandTime = recentCommandClock; if (!steppers::isRunning()) { mode = READY; } else if (homing_timeout.hasElapsed()) { @@ -128,9 +193,11 @@ void runCommandSlice() { } } if (mode == MOVING) { + recentCommandTime = recentCommandClock; if (!steppers::isRunning()) { mode = READY; } } if (mode == DELAY) { + recentCommandTime = recentCommandClock; // check timers if (delay_timeout.hasElapsed()) { mode = READY; @@ -139,46 +206,15 @@ void runCommandSlice() { if (mode == WAIT_ON_TOOL) { if (tool_wait_timeout.hasElapsed()) { mode = READY; - } else if (tool::getLock()) { - OutPacket& out = tool::getOutPacket(); - InPacket& in = tool::getInPacket(); - out.reset(); - out.append8(tool::getCurrentToolheadIndex()); - out.append8(SLAVE_CMD_GET_TOOL_STATUS); - tool::startTransaction(); - // WHILE: bounded by timeout in runToolSlice - while (!tool::isTransactionDone()) { - tool::runToolSlice(); - } - if (!in.hasError()) { - if (in.read8(1) & 0x01) { - mode = READY; - } - } - tool::releaseLock(); + } else if (isToolReady()) { + mode = READY; } } if (mode == WAIT_ON_PLATFORM) { - // FIXME: Duplicates most code from WAIT_ON_TOOL if (tool_wait_timeout.hasElapsed()) { mode = READY; - } else if (tool::getLock()) { - OutPacket& out = tool::getOutPacket(); - InPacket& in = tool::getInPacket(); - out.reset(); - out.append8(tool::getCurrentToolheadIndex()); - out.append8(SLAVE_CMD_IS_PLATFORM_READY); - tool::startTransaction(); - // WHILE: bounded by timeout in runToolSlice - while (!tool::isTransactionDone()) { - tool::runToolSlice(); - } - if (!in.hasError()) { - if (in.read8(1) != 0) { - mode = READY; - } - } - tool::releaseLock(); + } else if (isPlatformReady()) { + mode = READY; } } if (mode == READY) { @@ -361,9 +397,162 @@ void runCommandSlice() { } } } + } else if (command == HOST_CMD_MOOD_LIGHT_SET_RGB ) { + // check for completion + if (command_buffer.getLength() >= 21) { + command_buffer.pop(); // remove the command code + int32_t r = pop32(); + int32_t g = pop32(); + int32_t b = pop32(); + int32_t fadeSpeed = pop32(); + int32_t writeToEeprom = pop32(); +#ifdef HAS_MOOD_LIGHT + Motherboard::getBoard().MoodLightSetRGBColor((uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)fadeSpeed, (uint8_t)writeToEeprom); +#endif + } + } else if (command == HOST_CMD_MOOD_LIGHT_SET_HSB ) { + // check for completion + if (command_buffer.getLength() >= 17) { + command_buffer.pop(); // remove the command code + int32_t h = pop32(); + int32_t s = pop32(); + int32_t b = pop32(); + int32_t fadeSpeed = pop32(); +#ifdef HAS_MOOD_LIGHT + Motherboard::getBoard().MoodLightSetHSBColor((uint8_t)h, (uint8_t)s, (uint8_t)b, (uint8_t)fadeSpeed); +#endif + } + } else if (command == HOST_CMD_MOOD_LIGHT_PLAY_SCRIPT ) { + // check for completion + if (command_buffer.getLength() >= 9) { + command_buffer.pop(); // remove the command code + int32_t scriptId = pop32(); + int32_t writeToEeprom = pop32(); +#ifdef HAS_MOOD_LIGHT + Motherboard::getBoard().MoodLightPlayScript((uint8_t)scriptId, (uint8_t)writeToEeprom); +#endif + } } else { } } } } + + +#ifdef HAS_MOOD_LIGHT + +#define STOCHASTIC_PERCENT(v, a, b) (((v - a) / (b - a)) * 100.0) +#define MAX2(a,b) ((a >= b)?a:b) +#define STATUS_DIVISOR_TIME_PER_STATUS_CHANGE 4000 +#define RECENT_COMMAND_TIMEOUT 4000 * 10 + +void updateMoodStatus() { + MoodLightController moodLight = Motherboard::getBoard().getMoodLightController(); + + //If we're not set to the Bot Status Script, then there's no need to check anything + if ( moodLight.getLastScriptPlayed() != 0 ) return; + + //Implement a divisor so we don't get called on every turn, we don't + //want to overload the interrupt loop when we don't need frequent changes + statusDivisor ++; + + if ( statusDivisor < STATUS_DIVISOR_TIME_PER_STATUS_CHANGE ) return; + statusDivisor = 0; + + //Certain states don't require us to check as often, + //save some CPU cycles + + enum moodLightStatus lastMlStatus = moodLight.getLastStatus(); + + //If printing and recent commands, do nothing + if ((lastMlStatus == MOOD_LIGHT_STATUS_PRINTING) && + ( recentCommandTime >= (recentCommandClock - RECENT_COMMAND_TIMEOUT ))) return; + + enum moodLightStatus mlStatus = MOOD_LIGHT_STATUS_IDLE; + + //Get the status of the tool head and platform + + //Figure out how hot or cold we are + bool toolReady = isToolReady(); + bool platformReady = isPlatformReady(); + + OutPacket responsePacket; + uint16_t toolTemp=0, toolTempSetPoint=0, platformTemp=0, platformTempSetPoint=0; + + if (extruderControl(SLAVE_CMD_GET_TEMP, EXTDR_CMD_GET, responsePacket, 0)) + toolTemp = responsePacket.read16(1); + + if (extruderControl(SLAVE_CMD_GET_SP, EXTDR_CMD_GET, responsePacket, 0)) + toolTempSetPoint = responsePacket.read16(1); + + if (extruderControl(SLAVE_CMD_GET_PLATFORM_TEMP, EXTDR_CMD_GET, responsePacket, 0)) + platformTemp = responsePacket.read16(1); + + if (extruderControl(SLAVE_CMD_GET_PLATFORM_SP, EXTDR_CMD_GET, responsePacket, 0)) + platformTempSetPoint = responsePacket.read16(1); + + + float percentHotTool, percentHotPlatform; + + if ( toolTempSetPoint == 0 ) { + //We're cooling. 0% = 48C 100% = 240C + percentHotTool = STOCHASTIC_PERCENT((float)toolTemp, 48.0, 240.0); + if ( percentHotTool > 100.0 ) percentHotTool = 100.0; + if ( percentHotTool < 0.0 ) percentHotTool = 0.0; + } else { + //We're heating. 0% = 18C 100% = Set Point + percentHotTool = STOCHASTIC_PERCENT((float)toolTemp, 18.0, (float)toolTempSetPoint); + if ( percentHotTool > 100.0 ) percentHotTool = 100.0; + if ( percentHotTool < 0.0 ) percentHotTool = 0.0; + + if ( toolReady ) percentHotTool = 100.0; + else if ( percentHotTool >= 100.0 ) percentHotTool = 99.0; + } + + if ( platformTempSetPoint == 0 ) { + //We're cooling. 0% = 48C 100% = 120C + percentHotPlatform = STOCHASTIC_PERCENT((float)platformTemp, 48.0, 120.0); + if ( percentHotPlatform > 100.0 ) percentHotPlatform = 100.0; + if ( percentHotPlatform < 0.0 ) percentHotPlatform = 0.0; + } else { + //We're heating. 0% = 18C 100% = Set Point + percentHotPlatform = STOCHASTIC_PERCENT((float)platformTemp, 18.0, (float)platformTempSetPoint); + if ( percentHotPlatform > 100.0 ) percentHotPlatform = 100.0; + if ( percentHotPlatform < 0.0 ) percentHotPlatform = 0.0; + + if ( platformReady ) percentHotPlatform = 100.0; + else if ( percentHotPlatform >= 100.0 ) percentHotPlatform = 99.0; + } + + //Are we heating or cooling + bool heating = false; + if (( toolTempSetPoint != 0 ) || ( platformTempSetPoint != 0 )) heating = true; + + if ( heating ) { + //If we're heating and tool and platform are 100%, then we're not cooling anymore, we're printing + if (( percentHotTool >= 100.0 ) && ( percentHotPlatform >= 100.0 ) && + ( recentCommandTime >= (recentCommandClock - RECENT_COMMAND_TIMEOUT ))) { + mlStatus = MOOD_LIGHT_STATUS_PRINTING; + } else { + mlStatus = MOOD_LIGHT_STATUS_HEATING; + } + } else { + //If we're cooling and tool and platform are 0%, then we're not cooling anymore + if (( percentHotTool <= 0.0 ) && ( percentHotPlatform <= 0.0 )) { + mlStatus = MOOD_LIGHT_STATUS_IDLE; + } else { + mlStatus = MOOD_LIGHT_STATUS_COOLING; + } + } + + + float percentHot = MAX2(percentHotTool, percentHotPlatform); + + Motherboard::getBoard().getMoodLightController().displayStatus(mlStatus, percentHot); + + lastMlStatus = mlStatus; +} + +#endif + } diff --git a/firmware/src/Motherboard/Command.hh b/firmware/src/Motherboard/Command.hh index 3a34167..66209af 100644 --- a/firmware/src/Motherboard/Command.hh +++ b/firmware/src/Motherboard/Command.hh @@ -31,6 +31,8 @@ void reset(); /// Run the command thread slice. void runCommandSlice(); +void updateMoodStatus(); + /// Pause the command processor /// \param[in] pause If true, disable the command processor. If false, enable it. void pause(bool pause); diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index acebe3f..a891823 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -35,6 +35,10 @@ void setDefaults() { eeprom_write_byte((uint8_t*)eeprom::PLATFORM_TEMP,110); eeprom_write_byte((uint8_t*)eeprom::EXTRUDE_DURATION,1); eeprom_write_byte((uint8_t*)eeprom::EXTRUDE_RPM,19); + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_SCRIPT,0); + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_RED,255); + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_GREEN,255); + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_BLUE,255); } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index 4ccec87..4f42c37 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -58,11 +58,15 @@ enum { ESTOP_CONF_ACTIVE_LOW = 0x2 }; -const static uint16_t TOOL0_TEMP = 0x0080; -const static uint16_t TOOL1_TEMP = 0x0081; -const static uint16_t PLATFORM_TEMP = 0x0082; -const static uint16_t EXTRUDE_DURATION= 0x0083; -const static uint16_t EXTRUDE_RPM = 0x0084; +const static uint16_t TOOL0_TEMP = 0x0080; +const static uint16_t TOOL1_TEMP = 0x0081; +const static uint16_t PLATFORM_TEMP = 0x0082; +const static uint16_t EXTRUDE_DURATION = 0x0083; +const static uint16_t EXTRUDE_RPM = 0x0084; +const static uint16_t MOOD_LIGHT_SCRIPT = 0x0085; +const static uint16_t MOOD_LIGHT_CUSTOM_RED = 0x0086; +const static uint16_t MOOD_LIGHT_CUSTOM_GREEN = 0x0087; +const static uint16_t MOOD_LIGHT_CUSTOM_BLUE = 0x0088; /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/Motherboard/ExtruderControl.cc b/firmware/src/Motherboard/ExtruderControl.cc new file mode 100755 index 0000000..d0269b0 --- /dev/null +++ b/firmware/src/Motherboard/ExtruderControl.cc @@ -0,0 +1,73 @@ +/* + * Extruder Control Routine + * + * Copyright Dec, 2011 by Jetty840 + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + +#include "ExtruderControl.hh" + + +#define HOST_TOOL_RESPONSE_TIMEOUT_MS 50 + + +/// Send a packet to the extruder. If cmdType == EXTDR_CMD_SET, then "val" should +/// contain the value to be written otherwise val is ignored. +/// responsePacket is filled with the returned value +bool extruderControl(uint8_t command, enum extruderCommandType cmdType, + OutPacket& responsePacket, uint16_t val) { + + Timeout acquire_lock_timeout; + acquire_lock_timeout.start(HOST_TOOL_RESPONSE_TIMEOUT_MS); + while (!tool::getLock()) { + if (acquire_lock_timeout.hasElapsed()) { + return false; + } + } + OutPacket& out = tool::getOutPacket(); + InPacket& in = tool::getInPacket(); + out.reset(); + responsePacket.reset(); + + // Fill the query packet. The first byte is the toolhead index, and the + // second is the + out.append8(0); + out.append8(command); + if ( cmdType == EXTDR_CMD_SET ) out.append16(val); + + // Timeouts are handled inside the toolslice code; there's no need + // to check for timeouts on this loop. + tool::startTransaction(); + tool::releaseLock(); + // WHILE: bounded by tool timeout in runToolSlice + while (!tool::isTransactionDone()) { + tool::runToolSlice(); + } + if (in.getErrorCode() == PacketError::PACKET_TIMEOUT) { + return false; + } else { + // Copy payload back. Start from 0-- we need the response code. + for (uint8_t i = 0; i < in.getLength(); i++) { + responsePacket.append8(in.read8(i)); + } + } + + // Check that the extruder was able to process the request + if (!rcCompare(responsePacket.read8(0),RC_OK)) { + return false; + } + + return true; +} diff --git a/firmware/src/Motherboard/ExtruderControl.hh b/firmware/src/Motherboard/ExtruderControl.hh new file mode 100755 index 0000000..a6c8f3f --- /dev/null +++ b/firmware/src/Motherboard/ExtruderControl.hh @@ -0,0 +1,37 @@ +/* + * Extruder Control Routine + * + * Copyright Dec, 2011 by Jetty840 + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef EXTRUDERCONTROL_HH_ +#define EXTRUDERCONTROL_HH_ + +#include "Types.hh" +#include "Tool.hh" +#include "Timeout.hh" + + +enum extruderCommandType { + EXTDR_CMD_GET, + EXTDR_CMD_SET +}; + + +bool extruderControl(uint8_t command, enum extruderCommandType cmdType, + OutPacket& responsePacket, uint16_t val); + +#endif // EXTRUDERCONTROL_HH_ diff --git a/firmware/src/Motherboard/boards/mb24/Configuration.hh b/firmware/src/Motherboard/boards/mb24/Configuration.hh index 82c6d89..866a614 100644 --- a/firmware/src/Motherboard/boards/mb24/Configuration.hh +++ b/firmware/src/Motherboard/boards/mb24/Configuration.hh @@ -182,4 +182,11 @@ #define INTERFACE_BAR_PIN Pin(PortL,0) #define INTERFACE_DEBUG_PIN Pin(PortB,7) +//Pin mapping for Software I2C communication using analog pins +//as digital pins. +//This is primrarily for BlinkM MaxM, may not work for other I2C. +#define HAS_MOOD_LIGHT 1 +#define SOFTWARE_I2C_SDA_PIN Pin(PortK,0) //Pin d on the BlinkM +#define SOFTWARE_I2C_SCL_PIN Pin(PortK,1) //Pin c on the BlinkM + #endif // BOARDS_RRMBV12_CONFIGURATION_HH_ diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index f868fec..c17b460 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -28,7 +28,7 @@ #include "Commands.hh" #include "Eeprom.hh" #include "EepromMap.hh" - +#include /// Instantiate static motherboard instance Motherboard Motherboard::motherboard; @@ -41,13 +41,15 @@ Motherboard::Motherboard() : LCD_D1_PIN, LCD_D2_PIN, LCD_D3_PIN), + moodLightController(SOFTWARE_I2C_SDA_PIN, + SOFTWARE_I2C_SCL_PIN), interfaceBoard(buttonArray, lcd, INTERFACE_FOO_PIN, INTERFACE_BAR_PIN, &mainMenu, - &monitorMode) - + &monitorMode, + moodLightController) { /// Set up the stepper pins on board creation #if STEPPER_COUNT > 0 @@ -98,6 +100,8 @@ Motherboard::Motherboard() : void Motherboard::reset() { indicateError(0); // turn off blinker + moodLightController.start(); + // Init steppers uint8_t axis_invert = eeprom::getEeprom8(eeprom::AXIS_INVERSION, 0); // Z holding indicates that when the Z axis is not in @@ -215,6 +219,10 @@ void Motherboard::runMotherboardSlice() { } } +MoodLightController Motherboard::getMoodLightController() { + return moodLightController; +} + /// Timer one comparator match interrupt ISR(TIMER1_COMPA_vect) { @@ -249,6 +257,28 @@ uint8_t Motherboard::getCurrentError() { return blink_count; } +void Motherboard::MoodLightSetRGBColor(uint8_t r, uint8_t g, uint8_t b, uint8_t fadeSpeed, uint8_t writeToEeprom) { + if ( writeToEeprom ) { + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_RED, r); + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_GREEN,g); + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_BLUE, b); + } else { + moodLightController.blinkM.setFadeSpeed(fadeSpeed); + moodLightController.blinkM.fadeToRGB(r,g,b); + } +} + +void Motherboard::MoodLightSetHSBColor(uint8_t r, uint8_t g, uint8_t b, uint8_t fadeSpeed) { + moodLightController.blinkM.setFadeSpeed(fadeSpeed); + moodLightController.blinkM.fadeToHSB(r,g,b); +} + +void Motherboard::MoodLightPlayScript(uint8_t scriptId, uint8_t writeToEeprom) { + if ( writeToEeprom ) eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_SCRIPT,scriptId); + moodLightController.playScript(scriptId); +} + + /// Timer2 overflow cycles that the LED remains on while blinking #define OVFS_ON 18 /// Timer2 overflow cycles that the LED remains off while blinking @@ -271,6 +301,8 @@ ISR(TIMER2_OVF_vect) { blink_state = BLINK_OFF; blink_ovfs_remaining = OVFS_OFF; DEBUG_PIN.setValue(false); + if ( blink_count == ERR_ESTOP ) + Motherboard::getBoard().getMoodLightController().debugLightSetValue(false); } else if (blink_state == BLINK_OFF) { if (blinked_so_far >= blink_count) { blink_state = BLINK_PAUSE; @@ -279,12 +311,16 @@ ISR(TIMER2_OVF_vect) { blink_state = BLINK_ON; blink_ovfs_remaining = OVFS_ON; DEBUG_PIN.setValue(true); + if ( blink_count == ERR_ESTOP ) + Motherboard::getBoard().getMoodLightController().debugLightSetValue(true); } } else if (blink_state == BLINK_PAUSE) { blinked_so_far = 0; blink_state = BLINK_ON; blink_ovfs_remaining = OVFS_ON; DEBUG_PIN.setValue(true); + if ( blink_count == ERR_ESTOP ) + Motherboard::getBoard().getMoodLightController().debugLightSetValue(true); } } } diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.hh b/firmware/src/Motherboard/boards/mb24/Motherboard.hh index 6254b8f..88ecc7b 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.hh +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.hh @@ -28,6 +28,8 @@ #include "InterfaceBoard.hh" #include "LiquidCrystal.hh" #include "ButtonArray.hh" +#include "MoodLightController.hh" +#include "Errors.hh" /// Main class for Motherboard version 2.4+ (Gen4 electronics) @@ -64,6 +66,7 @@ private: ButtonArray buttonArray; LiquidCrystal lcd; InterfaceBoard interfaceBoard; + MoodLightController moodLightController; MainMenu mainMenu; ///< Main system menu SplashScreen splashScreen; ///< Displayed at startup @@ -99,6 +102,11 @@ public: /// Perform the timer interrupt routine. void doInterrupt(); + + MoodLightController getMoodLightController(); + void MoodLightSetRGBColor(uint8_t r, uint8_t g, uint8_t b, uint8_t fadeSpeed, uint8_t writeToEeprom); + void MoodLightSetHSBColor(uint8_t r, uint8_t g, uint8_t b, uint8_t fadeSpeed); + void MoodLightPlayScript(uint8_t scriptId, uint8_t writeToEeprom); }; #endif // BOARDS_MB24_MOTHERBOARD_HH_ diff --git a/firmware/src/shared/BlinkMMaxM.cc b/firmware/src/shared/BlinkMMaxM.cc new file mode 100755 index 0000000..ecde43a --- /dev/null +++ b/firmware/src/shared/BlinkMMaxM.cc @@ -0,0 +1,280 @@ +/* + * BlinkM MaxM Support over Software I2C + * + * Reference: + * http://thingm.com/products/blinkm-maxm.html + * + * + * Copyright Dec, 2011 by Jetty840 + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + +#include +#include +#include "BlinkMMaxM.hh" + +#define BLINKM_PRESENT_CHECK if ( ! blinkMIsPresent ) return + +void BlinkMMaxM::sendN(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4, uint8_t arg5, uint8_t arg6, uint8_t arg7, int countArgs) { + softI2C.start( blinkMAddr, false ); + softI2C.txByte( cmd ); + + if ( countArgs >= 1 ) softI2C.txByte( arg1 ); + if ( countArgs >= 2 ) softI2C.txByte( arg2 ); + if ( countArgs >= 3 ) softI2C.txByte( arg3 ); + if ( countArgs >= 4 ) softI2C.txByte( arg4 ); + if ( countArgs >= 5 ) softI2C.txByte( arg5 ); + if ( countArgs >= 6 ) softI2C.txByte( arg6 ); + if ( countArgs >= 7 ) softI2C.txByte( arg7 ); + + softI2C.stop(); +} + +void BlinkMMaxM::send0(uint8_t cmd) { + sendN(cmd, 0, 0, 0, 0, 0, 0, 0, 0); +} + +void BlinkMMaxM::send1(uint8_t cmd, uint8_t arg1) { + sendN(cmd, arg1, 0, 0, 0, 0, 0, 0, 1); +} + +void BlinkMMaxM::send2(uint8_t cmd, uint8_t arg1, uint8_t arg2) { + sendN(cmd, arg1, arg2, 0, 0, 0, 0, 0, 2); +} + +void BlinkMMaxM::send3(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3) { + sendN(cmd, arg1, arg2, arg3, 0, 0, 0, 0, 3); +} + +void BlinkMMaxM::send4(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4) { + sendN(cmd, arg1, arg2, arg3, arg4, 0, 0, 0, 4); +} + +void BlinkMMaxM::send5(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4, uint8_t arg5) { + sendN(cmd, arg1, arg2, arg3, arg4, arg5, 0, 0, 5); +} + +void BlinkMMaxM::send6(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4, uint8_t arg5, uint8_t arg6) { + sendN(cmd, arg1, arg2, arg3, arg4, arg5, arg6, 0, 6); +} + +void BlinkMMaxM::send7(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4, uint8_t arg5, uint8_t arg6, uint8_t arg7) { + sendN(cmd, arg1, arg2, arg3, arg4, arg5, arg6, arg7, 7); +} + +void BlinkMMaxM::recv1(uint8_t cmd, uint8_t *arg1) { + softI2C.start( blinkMAddr, false); + softI2C.txByte( cmd ); + softI2C.stop(); + softI2C.start( blinkMAddr, true ); + *arg1 = softI2C.rxByte(0); + softI2C.stop(); +} + +void BlinkMMaxM::recv2(uint8_t cmd, uint8_t *arg1, uint8_t *arg2) { + softI2C.start( blinkMAddr, false); + softI2C.txByte( cmd ); + softI2C.stop(); + softI2C.start( blinkMAddr, true ); + *arg1 = softI2C.rxByte(1); + *arg2 = softI2C.rxByte(0); + softI2C.stop(); +} + +void BlinkMMaxM::recv3(uint8_t cmd, uint8_t *arg1, uint8_t *arg2, uint8_t *arg3) { + softI2C.start( blinkMAddr, false); + softI2C.txByte( cmd ); + softI2C.stop(); + softI2C.start( blinkMAddr, true ); + *arg1 = softI2C.rxByte(1); + *arg2 = softI2C.rxByte(1); + *arg3 = softI2C.rxByte(0); + softI2C.stop(); +} + + +BlinkMMaxM::BlinkMMaxM() { +} + + +BlinkMMaxM::BlinkMMaxM(Pin sda, Pin scl) { + init(sda, scl); +} + + +void BlinkMMaxM::init(Pin sda, Pin scl) { + blinkMIsPresent = false; + blinkMAddr = 0x09; + softI2C.init(sda, scl); + + //Wait for BlinkM to come online + _delay_us(500000); + + //Override and set the BlinkM address back to the default (9), as it + //save wasting time trying to locate it + blinkMIsPresent = true; + setAddress(blinkMAddr); + + //Get the version to see if we have a BlinkM Present + int vers = getVersion(); + uint8_t major = vers >> 8; + uint8_t minor = vers & 0x00FF; + + blinkMIsPresent= false; + if ( major == 'a' ) { + if (( minor >= 'a' ) && ( minor <= 'd' )) + blinkMIsPresent = true; + } + + factoryReset(); +} + + +void BlinkMMaxM::stopScript() { + BLINKM_PRESENT_CHECK; + scriptPlaying = false; + send0('o'); +} + + +void BlinkMMaxM::setFadeSpeed( uint8_t f) { + BLINKM_PRESENT_CHECK; + + if ( f == fadeSpeed ) return; + + send1('f', f); + fadeSpeed = f; + + _delay_us(10000); +} + + +void BlinkMMaxM::setTimeAdjust(int8_t t) { + BLINKM_PRESENT_CHECK; + + //Convert the signed byte to unsigned + uint8_t v; + memcpy((void *)v, (void *)t, sizeof(int8_t)); + + send1('t', v); + _delay_us(10000); +} + + +uint8_t BlinkMMaxM::getFadeSpeed() { + if ( ! blinkMIsPresent ) return 0; + return fadeSpeed; +} + + +void BlinkMMaxM::fadeToRGB( uint8_t r, uint8_t g, uint8_t b ) { + BLINKM_PRESENT_CHECK; + if ( scriptPlaying ) stopScript(); + send3('c', r, g, b); +} + + +void BlinkMMaxM::setRGB( uint8_t r, uint8_t g, uint8_t b ) { + BLINKM_PRESENT_CHECK; + if ( scriptPlaying ) stopScript(); + send3('n', r, g, b); +} + + +void BlinkMMaxM::setAddress(uint8_t addr) { + BLINKM_PRESENT_CHECK; + + //Send to 0x00, the broadcast address + blinkMAddr = 0x00; + send4('A', addr, 0xD0, 0x0D, addr ); + blinkMAddr = addr; + _delay_us(50000); +} + + +int BlinkMMaxM::getVersion() { + if ( ! blinkMIsPresent ) return -1; + + uint8_t major, minor; + + recv2('Z', &major, &minor); + + return (major << 8) + minor; +} + + +void BlinkMMaxM::fadeToHSB( uint8_t h, uint8_t s, uint8_t b ) { + BLINKM_PRESENT_CHECK; + if ( scriptPlaying ) stopScript(); + send3('h', h, s, b); +} + + +void BlinkMMaxM::playScript(uint8_t scriptId, uint8_t reps) { + BLINKM_PRESENT_CHECK; + scriptPlaying = true; + send3('p', scriptId, reps, 0); +} + + +void BlinkMMaxM::getRGBColor(uint8_t *r, uint8_t *g, uint8_t *b) { + BLINKM_PRESENT_CHECK; + recv3('g', r, g, b); +} + + +void BlinkMMaxM::writeScript(uint8_t script_id, uint8_t len, uint8_t reps, scriptLine *lines) { + BLINKM_PRESENT_CHECK; + + for (uint8_t i = 0; i < len; i++ ) { + scriptLine l = lines[i]; + send7('W', script_id, i, l.duration, l.cmd[0], l.cmd[1], l.cmd[2], l.cmd[3]); + _delay_us(20000); + } + + send3('L', script_id, len, reps ); +} + + +void BlinkMMaxM::factoryReset() { + BLINKM_PRESENT_CHECK; + + //Set address to defaults + setAddress(0x09); + + //Write defaults + send5('B', + 0x01, //Play Script (0x01) + 0x00, //Script 0 + 0x00, //Repeat indefinately + 0x08, //Fade speed + 0x00); //Time adjust + + _delay_us(50000); + + scriptLine script[] = { + { 1, {'f', 10, 00, 00}}, //Fade Speed 10 + { 100, {'c', 0xff,0xff,0xff}}, //White + { 50, {'c', 0xff,0x00,0x00}}, //Red + { 50, {'c', 0x00,0xff,0x00}}, //Green + { 50, {'c', 0x00,0x00,0xff}}, //Blue + { 50, {'c', 0x00,0x00,0x00}}, //Off + }; + int len = 6; // number of script lines above + + writeScript( 0, len, 0, script); + + _delay_us(50000); +} diff --git a/firmware/src/shared/BlinkMMaxM.hh b/firmware/src/shared/BlinkMMaxM.hh new file mode 100755 index 0000000..5f97927 --- /dev/null +++ b/firmware/src/shared/BlinkMMaxM.hh @@ -0,0 +1,81 @@ +/* + * BlinkM MaxM Support over Software I2C + * + * Reference: + * http://thingm.com/products/blinkm-maxm.html + * + * + * Copyright Dec, 2011 by Jetty840 + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef BLINKMMAXM_HH_ +#define BLINKMMAXM_HH_ + +#include "SoftwareI2C.hh" + + +typedef struct _scriptLine { + uint8_t duration; + uint8_t cmd[4]; //command, arg1, arg2, arg3 +} scriptLine; + + +class BlinkMMaxM { +public: + BlinkMMaxM(); + BlinkMMaxM(Pin sda, Pin sdl); + void init(Pin sda, Pin sdl); + + bool blinkMIsPresent; + + bool scriptPlaying; + + void stopScript(); + void setFadeSpeed(uint8_t f); + void setTimeAdjust(int8_t t); + uint8_t getFadeSpeed(); + void fadeToRGB( uint8_t r, uint8_t g, uint8_t b ); + void setRGB(uint8_t r, uint8_t g, uint8_t b ); + void setAddress(uint8_t addr); + int getVersion(); + void fadeToHSB( uint8_t h, uint8_t s, uint8_t b ); + void playScript(uint8_t scriptId, uint8_t reps); + void getRGBColor(uint8_t *r, uint8_t *g, uint8_t *b); + void factoryReset(); + void writeScript(uint8_t script_id, uint8_t len, uint8_t reps, scriptLine *lines); + +private: + SoftwareI2C softI2C; + uint8_t blinkMAddr; + uint8_t fadeSpeed; + + void sendN(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4, uint8_t arg5, uint8_t arg6, uint8_t arg7, int countArgs); + void send0(uint8_t cmd); + void send1(uint8_t cmd, uint8_t arg1); + void send2(uint8_t cmd, uint8_t arg1, uint8_t arg2); + void send3(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3); + void send4(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4); + void send5(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4, uint8_t arg5); + void send6(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4, uint8_t arg5, uint8_t arg6); + void send7(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4, uint8_t arg5, uint8_t arg6, uint8_t arg7); + + void recv1(uint8_t cmd, uint8_t *arg1); + void recv2(uint8_t cmd, uint8_t *arg1, uint8_t *arg2); + void recv3(uint8_t cmd, uint8_t *arg1, uint8_t *arg2, uint8_t *arg3); + +}; + +#endif // BLINKMMAXM_HH_ diff --git a/firmware/src/shared/Commands.hh b/firmware/src/shared/Commands.hh index 5d8f6ce..03b30e9 100644 --- a/firmware/src/shared/Commands.hh +++ b/firmware/src/shared/Commands.hh @@ -104,6 +104,9 @@ #define HOST_CMD_QUEUE_POINT_NEW 142 #define HOST_CMD_STORE_HOME_POSITION 143 #define HOST_CMD_RECALL_HOME_POSITION 144 +#define HOST_CMD_MOOD_LIGHT_SET_RGB 210 +#define HOST_CMD_MOOD_LIGHT_SET_HSB 211 +#define HOST_CMD_MOOD_LIGHT_PLAY_SCRIPT 212 #define HOST_CMD_DEBUG_ECHO 0x70 diff --git a/firmware/src/shared/Interface.cc b/firmware/src/shared/Interface.cc index 3cc2137..18e2aac 100644 --- a/firmware/src/shared/Interface.cc +++ b/firmware/src/shared/Interface.cc @@ -68,6 +68,10 @@ void doUpdate() { board->doUpdate(); } +MoodLightController moodLightController() { + return board->moodLight; +} + } diff --git a/firmware/src/shared/Interface.hh b/firmware/src/shared/Interface.hh index a9bdaac..3431bde 100644 --- a/firmware/src/shared/Interface.hh +++ b/firmware/src/shared/Interface.hh @@ -23,6 +23,7 @@ #include "Menu.hh" #include "InterfaceBoard.hh" #include "LiquidCrystal.hh" +#include "MoodLightController.hh" #include "Types.hh" // TODO: This style interface is weird; find a way to replace it. @@ -66,6 +67,7 @@ void doUpdate(); /// much impact. micros_t getUpdateRate(); +MoodLightController moodLightController(); } #endif diff --git a/firmware/src/shared/InterfaceBoard.cc b/firmware/src/shared/InterfaceBoard.cc index ed926e1..f6dc7d8 100644 --- a/firmware/src/shared/InterfaceBoard.cc +++ b/firmware/src/shared/InterfaceBoard.cc @@ -10,12 +10,13 @@ InterfaceBoard::InterfaceBoard(ButtonArray& buttons_in, const Pin& foo_pin_in, const Pin& bar_pin_in, Screen* mainScreen_in, - Screen* buildScreen_in) : + Screen* buildScreen_in, + MoodLightController& moodLight_in) : lcd(lcd_in), buttons(buttons_in), foo_pin(foo_pin_in), - bar_pin(bar_pin_in) -{ + bar_pin(bar_pin_in), + moodLight(moodLight_in) { buildScreen = buildScreen_in; mainScreen = mainScreen_in; } diff --git a/firmware/src/shared/InterfaceBoard.hh b/firmware/src/shared/InterfaceBoard.hh index a57dbf0..eefa9b1 100644 --- a/firmware/src/shared/InterfaceBoard.hh +++ b/firmware/src/shared/InterfaceBoard.hh @@ -23,6 +23,7 @@ #include "Pin.hh" #include "ButtonArray.hh" #include "Menu.hh" +#include "MoodLightController.hh" /// Maximum number of screens that can be active at once. #define SCREEN_STACK_DEPTH 5 @@ -41,6 +42,7 @@ class InterfaceBoard { public: LiquidCrystal& lcd; ///< LCD to write to + MoodLightController& moodLight; ///< Mood Light to write to private: ButtonArray& buttons; ///< Button array to read from @@ -75,7 +77,8 @@ public: const Pin& foo_pin_in, const Pin& bar_pin_in, Screen* mainScreen_in, - Screen* buildScreen_in); + Screen* buildScreen_in, + MoodLightController& moodLight_in); /// Initialze the interface board. This needs to be called once /// at system startup (or reset). diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 93f8334..c5da93c 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -20,6 +20,7 @@ #include "EepromMap.hh" #include "Eeprom.hh" #include +#include "ExtruderControl.hh" #define HOST_PACKET_TIMEOUT_MS 20 @@ -30,55 +31,6 @@ int16_t overrideExtrudeSeconds = 0; -/// Send a packet to the extruder. If cmdType == EXTDR_CMD_SET, then "val" should -/// contain the value to be written otherwise val is ignored. -/// responsePacket is filled with the returned value -bool extruderControl(uint8_t command, enum extruderCommandType cmdType, - OutPacket& responsePacket, uint16_t val) { - - Timeout acquire_lock_timeout; - acquire_lock_timeout.start(HOST_TOOL_RESPONSE_TIMEOUT_MS); - while (!tool::getLock()) { - if (acquire_lock_timeout.hasElapsed()) { - return false; - } - } - OutPacket& out = tool::getOutPacket(); - InPacket& in = tool::getInPacket(); - out.reset(); - responsePacket.reset(); - - // Fill the query packet. The first byte is the toolhead index, and the - // second is the - out.append8(0); - out.append8(command); - if ( cmdType == EXTDR_CMD_SET ) out.append16(val); - - // Timeouts are handled inside the toolslice code; there's no need - // to check for timeouts on this loop. - tool::startTransaction(); - tool::releaseLock(); - // WHILE: bounded by tool timeout in runToolSlice - while (!tool::isTransactionDone()) { - tool::runToolSlice(); - } - if (in.getErrorCode() == PacketError::PACKET_TIMEOUT) { - return false; - } else { - // Copy payload back. Start from 0-- we need the response code. - for (uint8_t i = 0; i < in.getLength(); i++) { - responsePacket.append8(in.read8(i)); - } - } - - // Check that the extruder was able to process the request - if (!rcCompare(responsePacket.read8(0),RC_OK)) { - return false; - } - - return true; -} - void strcat(char *buf, const char* str) @@ -683,6 +635,202 @@ void ExtruderSetRpmScreen::notifyButtonPressed(ButtonArray::ButtonName button) { } } + +void MoodLightMode::reset() { + updatePhase = 0; +} + +void MoodLightMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar mood1[] = "Mood: "; + const static PROGMEM prog_uchar mood3_1[] = "(set RGB)"; + const static PROGMEM prog_uchar mood3_2[] = "(mood)"; + const static PROGMEM prog_uchar blank[] = " "; + const static PROGMEM prog_uchar moodNotPresent1[] = "Mood Light not"; + const static PROGMEM prog_uchar moodNotPresent2[] = "present!!"; + const static PROGMEM prog_uchar moodNotPresent3[] = "See Thingiverse"; + const static PROGMEM prog_uchar moodNotPresent4[] = " thing:15347"; + + //If we have no mood light, point to thingiverse to make one + if ( ! interface::moodLightController().blinkM.blinkMIsPresent ) { + //Try once more to restart the mood light controller + if ( ! interface::moodLightController().start() ) { + if ( forceRedraw ) { + lcd.clear(); + lcd.setCursor(0,0); + lcd.writeFromPgmspace(moodNotPresent1); + lcd.setCursor(0,1); + lcd.writeFromPgmspace(moodNotPresent2); + lcd.setCursor(0,2); + lcd.writeFromPgmspace(moodNotPresent3); + lcd.setCursor(0,3); + lcd.writeFromPgmspace(moodNotPresent4); + } + + return; + } + } + + if (forceRedraw) { + lcd.clear(); + lcd.setCursor(0,0); + lcd.writeFromPgmspace(mood1); + + lcd.setCursor(10,2); + lcd.writeFromPgmspace(mood3_2); + } + + //Redraw tool info + uint8_t scriptId = eeprom_read_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT); + + switch (updatePhase) { + case 0: + lcd.setCursor(6, 0); + lcd.writeFromPgmspace(blank); + lcd.setCursor(6, 0); + lcd.writeFromPgmspace(interface::moodLightController().scriptIdToStr(scriptId)); + break; + + case 1: + lcd.setCursor(0, 2); + if ( scriptId == 1 ) lcd.writeFromPgmspace(mood3_1); + else lcd.writeFromPgmspace(blank); + break; + } + + updatePhase++; + if (updatePhase > 1) { + updatePhase = 0; + } +} + + + +void MoodLightMode::notifyButtonPressed(ButtonArray::ButtonName button) { + uint8_t scriptId; + + if ( ! interface::moodLightController().blinkM.blinkMIsPresent ) interface::popScreen(); + + switch (button) { + case ButtonArray::OK: + //Change the script to the next script id + scriptId = eeprom_read_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT); + scriptId = interface::moodLightController().nextScriptId(scriptId); + eeprom_write_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT, scriptId); + interface::moodLightController().playScript(scriptId); + break; + + case ButtonArray::ZERO: + scriptId = eeprom_read_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT); + if ( scriptId == 1 ) + { + //Set RGB Values + interface::pushScreen(&moodLightSetRGBScreen); + } + + break; + + case ButtonArray::YPLUS: + case ButtonArray::YMINUS: + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + case ButtonArray::ZMINUS: + case ButtonArray::ZPLUS: + break; + + case ButtonArray::CANCEL: + interface::popScreen(); + break; + } +} + + +void MoodLightSetRGBScreen::reset() { + inputMode = 0; //Red + redrawScreen = false; + + red = eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_RED, 255);; + green = eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_GREEN, 255);; + blue = eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_BLUE, 255);; +} + +void MoodLightSetRGBScreen::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1_red[] = "Red:"; + const static PROGMEM prog_uchar message1_green[] = "Green:"; + const static PROGMEM prog_uchar message1_blue[] = "Blue:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + + if ((forceRedraw) || (redrawScreen)) { + lcd.clear(); + + lcd.setCursor(0,0); + if ( inputMode == 0 ) lcd.writeFromPgmspace(message1_red); + else if ( inputMode == 1 ) lcd.writeFromPgmspace(message1_green); + else if ( inputMode == 2 ) lcd.writeFromPgmspace(message1_blue); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + + redrawScreen = false; + } + + + // Redraw tool info + lcd.setCursor(0,1); + if ( inputMode == 0 ) lcd.writeInt(red, 3); + else if ( inputMode == 1 ) lcd.writeInt(green,3); + else if ( inputMode == 2 ) lcd.writeInt(blue, 3); +} + +void MoodLightSetRGBScreen::notifyButtonPressed(ButtonArray::ButtonName button) { + uint8_t *value = &red; + + if ( inputMode == 1 ) value = &green; + else if ( inputMode == 2 ) value = &blue; + + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + case ButtonArray::OK: + if ( inputMode < 2 ) { + inputMode ++; + redrawScreen = true; + } else { + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_RED, red); + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_GREEN,green); + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_BLUE, blue); + + //Set the color + interface::moodLightController().playScript(1); + + interface::popScreen(); + } + break; + case ButtonArray::ZPLUS: + // increment more + if (*value <= 245) *value += 10; + break; + case ButtonArray::ZMINUS: + // decrement more + if (*value >= 10) *value -= 10; + break; + case ButtonArray::YPLUS: + // increment less + if (*value <= 254) *value += 1; + break; + case ButtonArray::YMINUS: + // decrement less + if (*value >= 1) *value -= 1; + break; + + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } +} + + void SnakeMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar gameOver[] = "GAME OVER!"; @@ -1166,7 +1314,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 7; + itemCount = 8; reset(); } @@ -1176,6 +1324,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar jog[] = "Jog"; const static PROGMEM prog_uchar preheat[] = "Preheat"; const static PROGMEM prog_uchar extruder[] = "Extrude"; + const static PROGMEM prog_uchar moodlight[]= "Mood Light"; const static PROGMEM prog_uchar versions[] = "Version"; const static PROGMEM prog_uchar snake[] = "Snake Game"; @@ -1196,9 +1345,12 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(extruder); break; case 5: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(moodlight); break; case 6: + lcd.writeFromPgmspace(versions); + break; + case 7: lcd.writeFromPgmspace(snake); break; } @@ -1228,10 +1380,14 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&extruderMenu); break; case 5: + // Show Mood Light Mode + interface::pushScreen(&moodLightMode); + break; + case 6: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 6: + case 7: // Show build from SD screen interface::pushScreen(&snake); break; diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 2c73273..75e052e 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -5,11 +5,6 @@ #include "ButtonArray.hh" #include "LiquidCrystal.hh" -enum extruderCommandType { - EXTDR_CMD_GET, - EXTDR_CMD_SET -}; - /// The screen class defines a standard interface for anything that should /// be displayed on the LCD. class Screen { @@ -339,6 +334,42 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class MoodLightSetRGBScreen: public Screen { +private: + uint8_t red; + uint8_t green; + uint8_t blue; + + int inputMode; //0 = red, 1 = green, 2 = blue + bool redrawScreen; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + + +class MoodLightMode: public Screen { +private: + uint8_t updatePhase; + + MoodLightSetRGBScreen moodLightSetRGBScreen; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -356,6 +387,7 @@ private: PreheatMenu preheatMenu; ExtruderMode extruderMenu; VersionMode versionMode; + MoodLightMode moodLightMode; SnakeMode snake; }; diff --git a/firmware/src/shared/MoodLightController.cc b/firmware/src/shared/MoodLightController.cc new file mode 100755 index 0000000..7a8baea --- /dev/null +++ b/firmware/src/shared/MoodLightController.cc @@ -0,0 +1,301 @@ +/* + * Mood Light Controller + * + * Copyright Dec, 2011 by Jetty840 + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + +#include "MoodLightController.hh" + +#include "Configuration.hh" +#include "EepromMap.hh" +#include "Eeprom.hh" +#include + + +volatile uint8_t lastScriptPlayed = 0; +volatile enum moodLightStatus lastStatus=MOOD_LIGHT_STATUS_NOT_SET; + + + +MoodLightController::MoodLightController(Pin sda, Pin scl) { + eStopTriggered = false; + lastScriptPlayed = 0; + lastStatus = MOOD_LIGHT_STATUS_IDLE; + + _sda = sda; + _scl = scl; +} + + +//Returns true if BlinkM found, otherwise false + +bool MoodLightController::start() { + blinkM.init(_sda, _scl); + + //BlinkM's by default boot up running a script, stop it + blinkM.stopScript(); + +#ifdef HAS_MOOD_LIGHT + playScript(eeprom::getEeprom8(eeprom::MOOD_LIGHT_SCRIPT, 0)); +#endif + + return blinkM.blinkMIsPresent; +} + + +//There has to be a better way, however PROGMEM precludes it + +const static PROGMEM prog_uchar script0 [] = "Bot Status"; +const static PROGMEM prog_uchar script1 [] = "Custom RGB"; +const static PROGMEM prog_uchar script2 [] = "Off"; +const static PROGMEM prog_uchar script3 [] = "White"; +const static PROGMEM prog_uchar script4 [] = "Red"; +const static PROGMEM prog_uchar script5 [] = "Green"; +const static PROGMEM prog_uchar script6 [] = "Blue"; +const static PROGMEM prog_uchar script7 [] = "Cyan"; +const static PROGMEM prog_uchar script8 [] = "Magenta"; +const static PROGMEM prog_uchar script9 [] = "Yellow"; +const static PROGMEM prog_uchar script10[] = "Purple"; +const static PROGMEM prog_uchar script11[] = "Orange"; +const static PROGMEM prog_uchar script12[] = "Almond"; +const static PROGMEM prog_uchar script13[] = "Peach Puff"; +const static PROGMEM prog_uchar script14[] = "Mint Cream"; +const static PROGMEM prog_uchar script15[] = "Alice Blue"; +const static PROGMEM prog_uchar script16[] = "Lavender"; +const static PROGMEM prog_uchar script17[] = "Misty Rose"; +const static PROGMEM prog_uchar script18[] = "Slate Gray"; +const static PROGMEM prog_uchar script19[] = "Gray"; +const static PROGMEM prog_uchar script20[] = "Light Gray"; +const static PROGMEM prog_uchar script21[] = "MidnghtBlu"; +const static PROGMEM prog_uchar script22[] = "DeepSkyBlu"; +const static PROGMEM prog_uchar script23[] = "OliveGreen"; +const static PROGMEM prog_uchar script24[] = "ForstGreen"; +const static PROGMEM prog_uchar script25[] = "Gold"; +const static PROGMEM prog_uchar script26[] = "Hot Pink"; +const static PROGMEM prog_uchar script27[] = "Linen"; +const static PROGMEM prog_uchar script100[] = "WRGB"; +const static PROGMEM prog_uchar script101[] = "RGB"; +const static PROGMEM prog_uchar script102[] = "WhiteFlash"; +const static PROGMEM prog_uchar script103[] = "Red Flash"; +const static PROGMEM prog_uchar script104[] = "GreenFlash"; +const static PROGMEM prog_uchar script105[] = "Blue Flash"; +const static PROGMEM prog_uchar script106[] = "Cyan Flash"; +const static PROGMEM prog_uchar script107[] = "MgntaFlash"; +const static PROGMEM prog_uchar script108[] = "Yell Flash"; +const static PROGMEM prog_uchar script109[] = "Black"; +const static PROGMEM prog_uchar script110[] = "Hue Cycle"; +const static PROGMEM prog_uchar script111[] = "Mood Light"; +const static PROGMEM prog_uchar script112[] = "Candle"; +const static PROGMEM prog_uchar script113[] = "Water"; +const static PROGMEM prog_uchar script114[] = "Old Neon"; +const static PROGMEM prog_uchar script115[] = "Seasons"; +const static PROGMEM prog_uchar script116[] = "Thundrstrm"; +const static PROGMEM prog_uchar script117[] = "Stop Light"; +const static PROGMEM prog_uchar script118[] = "S. O. S."; +const static PROGMEM prog_uchar script_unknown[] = "UNKNOWN"; + +//There has to be a better way, however PROGMEM precludes it + +const PROGMEM prog_uchar *MoodLightController::scriptIdToStr(uint8_t scriptId) { + switch(scriptId) { + case 0: return script0; + case 1: return script1; + case 2: return script2; + case 3: return script3; + case 4: return script4; + case 5: return script5; + case 6: return script6; + case 7: return script7; + case 8: return script8; + case 9: return script9; + case 10: return script10; + case 11: return script11; + case 12: return script12; + case 13: return script13; + case 14: return script14; + case 15: return script15; + case 16: return script16; + case 17: return script17; + case 18: return script18; + case 19: return script19; + case 20: return script20; + case 21: return script21; + case 22: return script22; + case 23: return script23; + case 24: return script24; + case 25: return script25; + case 26: return script26; + case 27: return script27; + case 100: return script100; + case 101: return script101; + case 102: return script102; + case 103: return script103; + case 104: return script104; + case 105: return script105; + case 106: return script106; + case 107: return script107; + case 108: return script108; + case 109: return script109; + case 110: return script110; + case 111: return script111; + case 112: return script112; + case 113: return script113; + case 114: return script114; + case 115: return script115; + case 116: return script116; + case 117: return script117; + case 118: return script118; + default: + return script_unknown; + } +} + + +#define kBlinkMStart 100 +#define kBlinkMEnd 118 + +#define kDefinedColors 26 +typedef uint8_t ColorLookup[kDefinedColors][3]; + +const ColorLookup clut PROGMEM = { + {0, 0, 0 }, //Off + {255, 255, 255}, //White + {255, 0, 0 }, //Red + {0, 255, 0 }, //Green + {0, 0, 255}, //Blue + {0, 255, 255}, //Cyan + {255, 0, 255}, //Magenta + {255, 255, 0 }, //Yellow + {160, 32, 240}, //Purple + {255, 165, 0 }, //Orange + {255, 195, 165}, //Almond + {255, 178, 145}, //Peach Puff + {205, 255, 210}, //Mint Cream + {210, 208, 255}, //Alice Blue + {180, 180, 255}, //Lavender + {255, 208, 205}, //Misty Rose + {112, 138, 144}, //Slate Gray + {190, 190, 190}, //Gray + {211, 211, 211}, //Light Gray + {25, 25, 112}, //Midnight Blue + {0, 191, 255}, //Deep Sky Blue + {85, 137, 47 }, //Olive Green + {34, 139, 34 }, //Forest Green + {255, 215, 0 }, //Gold + {255, 105, 180}, //Hot Pink + {240, 240, 230} //Linen +}; + + + +//Given a scriptId, return the next scriptId +//There has to be a better way, however PROGMEM precludes it + +uint8_t MoodLightController::nextScriptId(uint8_t currentScriptId) { + if ( currentScriptId <= kDefinedColors ) return currentScriptId + 1; + if ( currentScriptId == (kDefinedColors + 1) ) return 100; + if (( currentScriptId >= kBlinkMStart ) && ( currentScriptId < kBlinkMEnd)) return currentScriptId + 1; + return 0; +} + + +void MoodLightController::playScript(uint8_t scriptId) { + lastScriptPlayed = scriptId; + + blinkM.stopScript(); + + blinkM.setFadeSpeed(8); + blinkM.setTimeAdjust(50); + + if ( scriptId == 0 ) { //Bot Status + blinkM.fadeToRGB(255, 255, 255); + } + else if ( scriptId == 1 ) { //Custom RGB (User Defined) +#ifdef HAS_MOOD_LIGHT + blinkM.fadeToRGB(eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_RED, 255), + eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_GREEN,255), + eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_BLUE, 255)); +#endif + } else if (( scriptId >= 2 ) && ( scriptId < (2 + kDefinedColors ))) { //Regular solid colors + uint8_t r, g, b; + + //Copy from PROGMEM + memcpy_P(&r, (const void*)&(clut[scriptId-2][0]), sizeof(uint8_t)); + memcpy_P(&g, (const void*)&(clut[scriptId-2][1]), sizeof(uint8_t)); + memcpy_P(&b, (const void*)&(clut[scriptId-2][2]), sizeof(uint8_t)); + + blinkM.fadeToRGB(r, g, b); + } + + else if (( scriptId >= kBlinkMStart ) && (scriptId <= kBlinkMEnd )) //BlinkM Predefined Scripts + blinkM.playScript(scriptId - (uint8_t)100, 0); + else playScript(3); //Default to white for an unknown color +} + + +//Should only be used with ERR_ESTOP as most other errors occur to frequently +//Only displays when the script playing is "Bot Status", i.e. 0 + +void MoodLightController::debugLightSetValue(bool value) { +#ifdef HAS_MOOD_LIGHT + if ( eeprom::getEeprom8(eeprom::MOOD_LIGHT_SCRIPT, 0) == (uint8_t) 0 ) { + eStopTriggered = true; + if ( value ) blinkM.setRGB(255, 0, 0); + else blinkM.setRGB(0, 0, 0); + } +#endif +} + + +void MoodLightController::displayStatus(enum moodLightStatus status, float percentHot) { + if ( status == lastStatus ) { + if (( status != MOOD_LIGHT_STATUS_HEATING ) && + ( status != MOOD_LIGHT_STATUS_COOLING )) return; + } + + if ( lastScriptPlayed == (uint8_t)0 ) { //Bot Status Script + if ( eStopTriggered ) status = MOOD_LIGHT_STATUS_ERROR; + + uint8_t tempColor = (uint8_t)(percentHot/100.0 * 255.0); + + switch(status) { + case MOOD_LIGHT_STATUS_IDLE: + case MOOD_LIGHT_STATUS_PRINTING: + blinkM.fadeToRGB(255, 255, 255); + break; + case MOOD_LIGHT_STATUS_HEATING: + case MOOD_LIGHT_STATUS_COOLING: + blinkM.fadeToRGB(tempColor, 0, 255 - tempColor); + break; + + case MOOD_LIGHT_STATUS_ERROR: + //Error condition. This is being handled by + //debugLightSetValue, so we do nothing. + break; + } + } + + lastStatus = status; +} + + +enum moodLightStatus MoodLightController::getLastStatus() { + return lastStatus; +} + +uint8_t MoodLightController::getLastScriptPlayed() { + return lastScriptPlayed; +} diff --git a/firmware/src/shared/MoodLightController.hh b/firmware/src/shared/MoodLightController.hh new file mode 100755 index 0000000..0713e9a --- /dev/null +++ b/firmware/src/shared/MoodLightController.hh @@ -0,0 +1,60 @@ +/* + * Mood Light Controller + * + * Copyright Dec, 2011 by Jetty840 + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef MOODLIGHTCONTROLLER_HH_ +#define MOODLIGHTCONTROLLER_HH_ + +#include "BlinkMMaxM.hh" +#include + + +enum moodLightStatus { + MOOD_LIGHT_STATUS_NOT_SET = 0, + MOOD_LIGHT_STATUS_IDLE, + MOOD_LIGHT_STATUS_HEATING, + MOOD_LIGHT_STATUS_COOLING, + MOOD_LIGHT_STATUS_PRINTING, + MOOD_LIGHT_STATUS_ERROR +}; + + +class MoodLightController { +public: + BlinkMMaxM blinkM; + + MoodLightController(Pin sda, Pin sdl); + + //Used for Menu + const PROGMEM prog_uchar *scriptIdToStr(uint8_t scriptId); + uint8_t nextScriptId(uint8_t currentScriptId); + + bool start(); + void playScript(uint8_t scriptId); + + void debugLightSetValue(bool value); + void displayStatus(enum moodLightStatus status, float percentHot); + enum moodLightStatus getLastStatus(); + uint8_t getLastScriptPlayed(); + +private: + bool eStopTriggered; + Pin _sda, _scl; +}; + +#endif // MOODLIGHTCONTROLLER_HH_ diff --git a/firmware/src/shared/SoftwareI2C.cc b/firmware/src/shared/SoftwareI2C.cc new file mode 100755 index 0000000..20e94fa --- /dev/null +++ b/firmware/src/shared/SoftwareI2C.cc @@ -0,0 +1,168 @@ +/* + * Software I2C Library To Run On Analog Pins + * This implements a master and is designed to work with the BlinkM. + * May or may not work with other I2C devices + * + * Reference: + * http://www.robot-electronics.co.uk/acatalog/I2C_Tutorial.html + * http://codinglab.blogspot.com/2008/10/i2c-on-avr-using-bit-banging.html + * + * + * Copyright Dec, 2011 by Jetty840 + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + + +#include +#include "SoftwareI2C.hh" + + +//Pin high and low definitions +#define SCL_LOW {sclPin.setValue(false); sclPin.setDirection(true);} +#define SCL_HIGH {sclPin.setDirection(false); sclPin.setValue(true);} +#define SDA_LOW {sdaPin.setValue(false); sdaPin.setDirection(true);} +#define SDA_HIGH {sdaPin.setDirection(false); sdaPin.setValue(true);} + +#define kI2CBitTimeuS 50 //50 microseconds +#define I2C_DLY _delay_us(kI2CBitTimeuS) + + +//Transmit 1 bit + +void SoftwareI2C::txBit(uint8_t b) { + //Set up the data bit + if ( b ) SDA_HIGH //; in macro + else SDA_LOW //; in macro + + //Clock it to the slave + SCL_HIGH; + I2C_DLY; + + SCL_LOW; + I2C_DLY; + + SDA_LOW; + I2C_DLY; +} + + +//Receive 1 bit + +uint8_t SoftwareI2C::rxBit(void) { + SDA_HIGH; + SCL_HIGH; + I2C_DLY; + + uint8_t b = sdaPin.getValue(); + + SCL_LOW; + I2C_DLY; + + return b; +} + + +SoftwareI2C::SoftwareI2C() { +} + + +SoftwareI2C::SoftwareI2C(Pin sda, Pin sdl) { + init(sda, sdl); +} + + +void SoftwareI2C::init(Pin sda, Pin scl) { + //Store pins for use later + sclPin = scl; + sdaPin = sda; + + //These should really be set high at exactly the same time + //Set the bus to inactive + SDA_HIGH; + SCL_HIGH; + + I2C_DLY; +} + + + +//Start a transmission to an address. readReq specifies wether it's +//a read or write transmission + +uint8_t SoftwareI2C::start(uint8_t address, bool readReq) { + //These should really be set high at exactly the same time + SDA_HIGH; + SCL_HIGH; + I2C_DLY; + + //Signal the start of a transmission + + //Set data low + SDA_LOW; + I2C_DLY; + + //Set clock low + SCL_LOW; + I2C_DLY; + + //Shift the address by 1 bit, and add on the read/write bit + uint8_t addr = address << 1; + if ( readReq ) addr |= 0x01; + + //Transmit the address byte + return txByte(addr); +} + + +//Finish the transmission + +uint8_t SoftwareI2C::stop(void) { + //Signal the end of a transmission + SCL_HIGH; + I2C_DLY; + + SDA_HIGH; + I2C_DLY; +} + + +//Transmit one byte + +uint8_t SoftwareI2C::txByte(uint8_t c) { + for (int i = 0; i < 8; i ++, c = c << 1) + txBit(c & 0x80); + + return rxBit(); +} + + +//Receive one byte + +//When reading a number of bytes in succession, ack is set to 1 +//for every byte except the last byte + +uint8_t SoftwareI2C::rxByte(uint8_t ack) { + uint8_t ret = 0; + + for (int i = 0; i < 8; i ++ ) { + ret = ret << 1; + ret |= rxBit(); + } + + txBit( ! ack ); + I2C_DLY; + + return ret; +} diff --git a/firmware/src/shared/SoftwareI2C.hh b/firmware/src/shared/SoftwareI2C.hh new file mode 100755 index 0000000..62f34d3 --- /dev/null +++ b/firmware/src/shared/SoftwareI2C.hh @@ -0,0 +1,52 @@ +/* + * Software I2C Library To Run On Analog Pins + * This implements a master and is designed to work with the BlinkM. + * May or may not work with other I2C devices + * + * Reference: + * http://www.robot-electronics.co.uk/acatalog/I2C_Tutorial.html + * http://codinglab.blogspot.com/2008/10/i2c-on-avr-using-bit-banging.html + * + * + * Copyright Dec, 2011 by Jetty840 + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + + +#ifndef SOFTWAREI2C_HH_ +#define SOFTWAREI2C_HH_ + +#include "Pin.hh" + +class SoftwareI2C { +public: + SoftwareI2C(); + SoftwareI2C(Pin sda, Pin sdl); + + void init(Pin sda, Pin scl); + + uint8_t start(uint8_t address, bool readReq); + uint8_t stop(void); + uint8_t txByte(uint8_t c); + uint8_t rxByte(uint8_t ack); + +private: + Pin sdaPin, sclPin; + + void txBit(uint8_t b); + uint8_t rxBit(void); +}; + +#endif // SOFTWAREI2C_HH_ From e9ea3e2215456b19d3540d4526d6a09c47a5a344 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Tue, 3 Jan 2012 18:30:02 -0700 Subject: [PATCH 10/61] Added Home Axis Menu for X/Y/Z to Gen 4 Interface --- firmware/src/shared/Menu.cc | 90 ++++++++++++++++++++++++++++++++++--- firmware/src/shared/Menu.hh | 15 +++++++ 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index c5da93c..ed663e4 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1314,7 +1314,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 8; + itemCount = 9; reset(); } @@ -1324,6 +1324,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar jog[] = "Jog"; const static PROGMEM prog_uchar preheat[] = "Preheat"; const static PROGMEM prog_uchar extruder[] = "Extrude"; + const static PROGMEM prog_uchar homeAxis[] = "Home Axis"; const static PROGMEM prog_uchar moodlight[]= "Mood Light"; const static PROGMEM prog_uchar versions[] = "Version"; const static PROGMEM prog_uchar snake[] = "Snake Game"; @@ -1345,12 +1346,15 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(extruder); break; case 5: - lcd.writeFromPgmspace(moodlight); + lcd.writeFromPgmspace(homeAxis); break; case 6: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(moodlight); break; case 7: + lcd.writeFromPgmspace(versions); + break; + case 8: lcd.writeFromPgmspace(snake); break; } @@ -1380,14 +1384,18 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&extruderMenu); break; case 5: + // Show home axis + interface::pushScreen(&homeAxisMode); + break; + case 6: // Show Mood Light Mode interface::pushScreen(&moodLightMode); break; - case 6: + case 7: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 7: + case 8: // Show build from SD screen interface::pushScreen(&snake); break; @@ -1725,4 +1733,76 @@ void PreheatMenu::handleSelect(uint8_t index) { } } +void HomeAxisMode::reset() { +} + +void HomeAxisMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar home1[] = "Home Axis: "; + const static PROGMEM prog_uchar home2[] = " Y Z"; + const static PROGMEM prog_uchar home3[] = "X X "; + const static PROGMEM prog_uchar home4[] = " Y Z"; + + if (forceRedraw) { + lcd.clear(); + lcd.setCursor(0,0); + lcd.writeFromPgmspace(home1); + + lcd.setCursor(0,1); + lcd.writeFromPgmspace(home2); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(home3); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(home4); + } +} + +void HomeAxisMode::home(ButtonArray::ButtonName direction) { + uint8_t axis = 0; + bool maximums; + + switch(direction) { + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + axis = 0x01; + maximums = false; + break; + case ButtonArray::YMINUS: + case ButtonArray::YPLUS: + axis = 0x02; + maximums = false; + break; + case ButtonArray::ZMINUS: + case ButtonArray::ZPLUS: + axis = 0x04; + maximums = true; + break; + } + + steppers::startHoming(maximums, axis, (uint32_t)2000); +} + +void HomeAxisMode::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::YMINUS: + case ButtonArray::ZMINUS: + case ButtonArray::YPLUS: + case ButtonArray::ZPLUS: + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + home(button); + break; + case ButtonArray::ZERO: + case ButtonArray::OK: + case ButtonArray::CANCEL: + steppers::abort(); + steppers::enableAxis(0, false); + steppers::enableAxis(1, false); + steppers::enableAxis(2, false); + interface::popScreen(); + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 75e052e..cf42031 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -370,6 +370,20 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class HomeAxisMode: public Screen { +private: + void home(ButtonArray::ButtonName direction); + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -386,6 +400,7 @@ private: JogMode jogger; PreheatMenu preheatMenu; ExtruderMode extruderMenu; + HomeAxisMode homeAxisMode; VersionMode versionMode; MoodLightMode moodLightMode; SnakeMode snake; From 47ebca64b9aa123de8b9f935c3ad331129cc243d Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Tue, 3 Jan 2012 18:36:26 -0700 Subject: [PATCH 11/61] Bug Fix: Steppers no long hold when exiting the Jog Menu and Extrude Menu to reduce heating --- firmware/src/shared/Menu.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index ed663e4..fed65f4 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -349,6 +349,10 @@ void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { jog(button); break; case ButtonArray::CANCEL: + steppers::abort(); + steppers::enableAxis(0, false); + steppers::enableAxis(1, false); + steppers::enableAxis(2, false); interface::popScreen(); break; } @@ -527,6 +531,8 @@ void ExtruderMode::notifyButtonPressed(ButtonArray::ButtonName button) { extrude((seconds_t)(zReverse * lastDirection * extrudeSeconds), false); break; case ButtonArray::CANCEL: + steppers::abort(); + steppers::enableAxis(3, false); interface::popScreen(); break; } From 6d966ece9f20a1e35cb166c085cb9c49fd4ca5e0 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Tue, 3 Jan 2012 19:11:05 -0700 Subject: [PATCH 12/61] Added Steppers Menu item in G4 Interface for enabling / disabling steppers. Enables/disables x,y,z,tool0. --- firmware/src/Motherboard/Steppers.cc | 7 +++ firmware/src/Motherboard/Steppers.hh | 4 ++ firmware/src/shared/Menu.cc | 73 +++++++++++++++++++++++-- firmware/src/shared/Menu.hh | 12 ++++ firmware/src/shared/StepperAxis.cc | 4 ++ firmware/src/shared/StepperAxis.hh | 4 ++ firmware/src/shared/StepperInterface.cc | 5 ++ firmware/src/shared/StepperInterface.hh | 3 + 8 files changed, 107 insertions(+), 5 deletions(-) diff --git a/firmware/src/Motherboard/Steppers.cc b/firmware/src/Motherboard/Steppers.cc index 23707a8..ae37ee1 100644 --- a/firmware/src/Motherboard/Steppers.cc +++ b/firmware/src/Motherboard/Steppers.cc @@ -134,6 +134,13 @@ void enableAxis(uint8_t index, bool enable) { } } +/// Report if the given axis is enabled or disabled +bool isEnabledAxis(uint8_t index) { + if (index < STEPPER_COUNT) { + axes[index].isEnabledStepper(); + } +} + bool doInterrupt() { if (is_running) { if (intervals_remaining-- == 0) { diff --git a/firmware/src/Motherboard/Steppers.hh b/firmware/src/Motherboard/Steppers.hh index 6fc5ce5..5389beb 100644 --- a/firmware/src/Motherboard/Steppers.hh +++ b/firmware/src/Motherboard/Steppers.hh @@ -47,6 +47,10 @@ namespace steppers { /// \param[in] enable If true, enable the axis. If false, disable. void enableAxis(uint8_t index, bool enable); + /// Reports if the axis is enabled or not + /// \param[in] index Index of the axis to enable or disable + bool isEnabledAxis(uint8_t index); + /// Instruct the stepper subsystem to move the machine to the /// given position. /// \param[in] target Position to move to diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index fed65f4..a74d535 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1320,7 +1320,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 9; + itemCount = 10; reset(); } @@ -1331,6 +1331,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar preheat[] = "Preheat"; const static PROGMEM prog_uchar extruder[] = "Extrude"; const static PROGMEM prog_uchar homeAxis[] = "Home Axis"; + const static PROGMEM prog_uchar steppersS[]= "Steppers"; const static PROGMEM prog_uchar moodlight[]= "Mood Light"; const static PROGMEM prog_uchar versions[] = "Version"; const static PROGMEM prog_uchar snake[] = "Snake Game"; @@ -1355,12 +1356,15 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(homeAxis); break; case 6: - lcd.writeFromPgmspace(moodlight); + lcd.writeFromPgmspace(steppersS); break; case 7: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(moodlight); break; case 8: + lcd.writeFromPgmspace(versions); + break; + case 9: lcd.writeFromPgmspace(snake); break; } @@ -1394,14 +1398,18 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&homeAxisMode); break; case 6: + // Show steppers menu + interface::pushScreen(&steppersMenu); + break; + case 7: // Show Mood Light Mode interface::pushScreen(&moodLightMode); break; - case 7: + case 8: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 8: + case 9: // Show build from SD screen interface::pushScreen(&snake); break; @@ -1811,4 +1819,59 @@ void HomeAxisMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } +SteppersMenu::SteppersMenu() { + itemCount = 4; + reset(); +} + +void SteppersMenu::resetState() { + if (( steppers::isEnabledAxis(0) ) || + ( steppers::isEnabledAxis(1) ) || + ( steppers::isEnabledAxis(2) ) || + ( steppers::isEnabledAxis(3) )) itemIndex = 3; + else itemIndex = 2; + firstItemIndex = 2; +} + +void SteppersMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar title[] = "Stepper Motors:"; + const static PROGMEM prog_uchar disable[] = "Disable"; + const static PROGMEM prog_uchar enable[] = "Enable"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(title); + break; + case 1: + break; + case 2: + lcd.writeFromPgmspace(disable); + break; + case 3: + lcd.writeFromPgmspace(enable); + break; + } +} + +void SteppersMenu::handleSelect(uint8_t index) { + switch (index) { + case 2: + //Disable Steppers + steppers::enableAxis(0, false); + steppers::enableAxis(1, false); + steppers::enableAxis(2, false); + steppers::enableAxis(3, false); + interface::popScreen(); + break; + case 3: + //Enable Steppers + steppers::enableAxis(0, true); + steppers::enableAxis(1, true); + steppers::enableAxis(2, true); + steppers::enableAxis(3, true); + interface::popScreen(); + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index cf42031..8037dfd 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -384,6 +384,17 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class SteppersMenu: public Menu { +public: + SteppersMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -401,6 +412,7 @@ private: PreheatMenu preheatMenu; ExtruderMode extruderMenu; HomeAxisMode homeAxisMode; + SteppersMenu steppersMenu; VersionMode versionMode; MoodLightMode moodLightMode; SnakeMode snake; diff --git a/firmware/src/shared/StepperAxis.cc b/firmware/src/shared/StepperAxis.cc index c69bce1..943d1e1 100644 --- a/firmware/src/shared/StepperAxis.cc +++ b/firmware/src/shared/StepperAxis.cc @@ -41,6 +41,10 @@ void StepperAxis::enableStepper(bool enable) { interface->setEnabled(enable); } +bool StepperAxis::isEnabledStepper() { + return interface->getEnabled(); +} + void StepperAxis::reset() { position = 0; minimum = 0; diff --git a/firmware/src/shared/StepperAxis.hh b/firmware/src/shared/StepperAxis.hh index 6d13ef7..5403e40 100644 --- a/firmware/src/shared/StepperAxis.hh +++ b/firmware/src/shared/StepperAxis.hh @@ -70,6 +70,10 @@ public: /// \param[in] enable If true, enable the axis; otherwise, disable it. void enableStepper(bool enable); + /// Reports if the stepper motor driver on the given axis is enabled + /// \param[in] returns true if enabled, otherwise false + bool isEnabledStepper(); + /// Reset to initial state void reset(); diff --git a/firmware/src/shared/StepperInterface.cc b/firmware/src/shared/StepperInterface.cc index e769225..2b508fe 100644 --- a/firmware/src/shared/StepperInterface.cc +++ b/firmware/src/shared/StepperInterface.cc @@ -50,6 +50,11 @@ void StepperInterface::setEnabled(bool enabled) { enable_pin.setValue(!enabled); } +bool StepperInterface::getEnabled() { + // The A3982 stepper driver chip has an inverted enable. + return ! enable_pin.getValue(); +} + bool StepperInterface::isAtMaximum() { if (max_pin.isNull()) return false; bool v = max_pin.getValue(); diff --git a/firmware/src/shared/StepperInterface.hh b/firmware/src/shared/StepperInterface.hh index f22aa44..9ce21c9 100644 --- a/firmware/src/shared/StepperInterface.hh +++ b/firmware/src/shared/StepperInterface.hh @@ -68,6 +68,9 @@ public: /// \param[in] True to enable the motor void setEnabled(bool enabled); + /// Returns true if the stepper motor is enabled + bool getEnabled(); + /// Check if the maximum endstop has been triggered for this axis. /// \return True if the axis has triggered its maximum endstop bool isAtMaximum(); From 2a3d97eae866179459002f434651e7c4891c2845 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Tue, 3 Jan 2012 20:23:16 -0700 Subject: [PATCH 13/61] Added Test End Stops menu item to G4 Interface. Used for verifying end stops are working, useful for verifying connections on the motherboard have been put back correctly after servicing, and avoiding slamming bot into an axis because of a missing connection. --- firmware/src/Motherboard/Steppers.cc | 16 +++++++ firmware/src/Motherboard/Steppers.hh | 6 +++ firmware/src/shared/Menu.cc | 69 ++++++++++++++++++++++++++-- firmware/src/shared/Menu.hh | 14 ++++++ firmware/src/shared/StepperAxis.cc | 10 ++++ firmware/src/shared/StepperAxis.hh | 9 ++++ 6 files changed, 121 insertions(+), 3 deletions(-) diff --git a/firmware/src/Motherboard/Steppers.cc b/firmware/src/Motherboard/Steppers.cc index ae37ee1..dce666f 100644 --- a/firmware/src/Motherboard/Steppers.cc +++ b/firmware/src/Motherboard/Steppers.cc @@ -141,6 +141,22 @@ bool isEnabledAxis(uint8_t index) { } } +bool isAtMaximum(uint8_t index) { + if (index < STEPPER_COUNT) { + return axes[index].isAtMaximum(); + } + return false; +} + + +bool isAtMinimum(uint8_t index) { + if (index < STEPPER_COUNT) { + return axes[index].isAtMinimum(); + } + return false; +} + + bool doInterrupt() { if (is_running) { if (intervals_remaining-- == 0) { diff --git a/firmware/src/Motherboard/Steppers.hh b/firmware/src/Motherboard/Steppers.hh index 5389beb..92f6895 100644 --- a/firmware/src/Motherboard/Steppers.hh +++ b/firmware/src/Motherboard/Steppers.hh @@ -96,6 +96,12 @@ namespace steppers { /// through the entire build. If false, it will be /// disabled when not moving. void setHoldZ(bool holdZ); + + //Returns true if the end stop is current depressed + bool isAtMaximum(uint8_t index); + + //Returns true if the end stop is current depressed + bool isAtMinimum(uint8_t index); }; #endif // STEPPERS_HH_ diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index a74d535..c882964 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1320,7 +1320,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 10; + itemCount = 11; reset(); } @@ -1333,6 +1333,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar homeAxis[] = "Home Axis"; const static PROGMEM prog_uchar steppersS[]= "Steppers"; const static PROGMEM prog_uchar moodlight[]= "Mood Light"; + const static PROGMEM prog_uchar endStops[] = "Test End Stops"; const static PROGMEM prog_uchar versions[] = "Version"; const static PROGMEM prog_uchar snake[] = "Snake Game"; @@ -1362,9 +1363,12 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(moodlight); break; case 8: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(endStops); break; case 9: + lcd.writeFromPgmspace(versions); + break; + case 10: lcd.writeFromPgmspace(snake); break; } @@ -1406,10 +1410,14 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&moodLightMode); break; case 8: + // Show test end stops menu + interface::pushScreen(&testEndStopsMode); + break; + case 9: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 9: + case 10: // Show build from SD screen interface::pushScreen(&snake); break; @@ -1874,4 +1882,59 @@ void SteppersMenu::handleSelect(uint8_t index) { } } +void TestEndStopsMode::reset() { +} + +void TestEndStopsMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar test1[] = "Test End Stops: "; + const static PROGMEM prog_uchar test2[] = "(press end stop)"; + const static PROGMEM prog_uchar test3[] = "XMin:N YMin:N"; + const static PROGMEM prog_uchar test4[] = "ZMax:N"; + const static PROGMEM prog_uchar strY[] = "Y"; + const static PROGMEM prog_uchar strN[] = "N"; + + if (forceRedraw) { + lcd.clear(); + lcd.setCursor(0,0); + lcd.writeFromPgmspace(test1); + + lcd.setCursor(0,1); + lcd.writeFromPgmspace(test2); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(test3); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(test4); + } + + lcd.setCursor(5, 2); + if ( steppers::isAtMinimum(0) ) lcd.writeFromPgmspace(strY); + else lcd.writeFromPgmspace(strN); + + lcd.setCursor(15, 2); + if ( steppers::isAtMinimum(1) ) lcd.writeFromPgmspace(strY); + else lcd.writeFromPgmspace(strN); + + lcd.setCursor(5, 3); + if ( steppers::isAtMaximum(2) ) lcd.writeFromPgmspace(strY); + else lcd.writeFromPgmspace(strN); +} + +void TestEndStopsMode::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::YMINUS: + case ButtonArray::ZMINUS: + case ButtonArray::YPLUS: + case ButtonArray::ZPLUS: + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + case ButtonArray::ZERO: + case ButtonArray::OK: + case ButtonArray::CANCEL: + interface::popScreen(); + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 8037dfd..7f905a1 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -395,6 +395,19 @@ protected: void handleSelect(uint8_t index); }; +class TestEndStopsMode: public Screen { +private: + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -413,6 +426,7 @@ private: ExtruderMode extruderMenu; HomeAxisMode homeAxisMode; SteppersMenu steppersMenu; + TestEndStopsMode testEndStopsMode; VersionMode versionMode; MoodLightMode moodLightMode; SnakeMode snake; diff --git a/firmware/src/shared/StepperAxis.cc b/firmware/src/shared/StepperAxis.cc index 943d1e1..9bb3aa2 100644 --- a/firmware/src/shared/StepperAxis.cc +++ b/firmware/src/shared/StepperAxis.cc @@ -142,3 +142,13 @@ bool StepperAxis::doHoming(const int32_t intervals) { } return true; } + + +bool StepperAxis::isAtMaximum() { + return interface->isAtMaximum(); +} + + +bool StepperAxis::isAtMinimum() { + return interface->isAtMinimum(); +} diff --git a/firmware/src/shared/StepperAxis.hh b/firmware/src/shared/StepperAxis.hh index 5403e40..a74999b 100644 --- a/firmware/src/shared/StepperAxis.hh +++ b/firmware/src/shared/StepperAxis.hh @@ -85,6 +85,15 @@ public: /// \param[in] intervals Intervals that have passed since the previous interrupt /// \return True if the axis is still homing. bool doHoming(const int32_t intervals); + + /// Check if the maximum endstop has been triggered for this axis. + /// \return True if the axis has triggered its maximum endstop + bool isAtMaximum(); + + /// Check if the minimum endstop has been triggered for this axis. + /// \return True if the axis has triggered its minimum endstop + bool isAtMinimum(); + }; #endif // STEPPERAXIS_HH From 8606f47af732776e7297ac96d78946f96646a755 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Tue, 3 Jan 2012 21:23:08 -0700 Subject: [PATCH 14/61] Adding Page Up / Page Down support with the Z+ / Z- keys. This change is a slight alteration to commit 9bd5fa5 by Eried. Menu.cc is not located in boards/mb24, this change reflects that. Credit for the original code goes to Eried. --- firmware/src/shared/Menu.cc | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index c882964..a4c9dcb 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -29,6 +29,8 @@ #define HOST_TOOL_RESPONSE_TIMEOUT_MS 50 #define HOST_TOOL_RESPONSE_TIMEOUT_MICROS (1000L*HOST_TOOL_RESPONSE_TIMEOUT_MS) +#define MAX_ITEMS_PER_SCREEN 4 + int16_t overrideExtrudeSeconds = 0; @@ -1243,6 +1245,7 @@ void Menu::handleCancel() { } void Menu::notifyButtonPressed(ButtonArray::ButtonName button) { + uint8_t steps = MAX_ITEMS_PER_SCREEN; switch (button) { case ButtonArray::ZERO: case ButtonArray::OK: @@ -1252,18 +1255,24 @@ void Menu::notifyButtonPressed(ButtonArray::ButtonName button) { handleCancel(); break; case ButtonArray::YMINUS: + steps = 1; case ButtonArray::ZMINUS: // increment index - if (itemIndex < itemCount - 1) { - itemIndex++; - } + if (itemIndex < itemCount - steps) + itemIndex+=steps; + else if (itemIndex==itemCount-1) + itemIndex=firstItemIndex; + else itemIndex=itemCount-1; break; case ButtonArray::YPLUS: + steps = 1; case ButtonArray::ZPLUS: // decrement index - if (itemIndex > firstItemIndex) { - itemIndex--; - } + if (itemIndex-steps > firstItemIndex) + itemIndex-=steps; + else if (itemIndex==firstItemIndex) + itemIndex=itemCount - 1; + else itemIndex=firstItemIndex; break; case ButtonArray::XMINUS: @@ -1274,7 +1283,7 @@ void Menu::notifyButtonPressed(ButtonArray::ButtonName button) { CancelBuildMenu::CancelBuildMenu() { - itemCount = 4; + itemCount = MAX_ITEMS_PER_SCREEN; reset(); } From ce3c87098e227eb52dedfa476afe2584c691cd3a Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 4 Jan 2012 15:51:35 -0700 Subject: [PATCH 15/61] Added scrolling for long file names (<=31) chars to the build menu. --- firmware/src/shared/Menu.cc | 54 ++++++++++++++++++++++++++++++++++--- firmware/src/shared/Menu.hh | 9 +++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index a4c9dcb..b4e5b54 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1435,10 +1435,15 @@ void MainMenu::handleSelect(uint8_t index) { SDMenu::SDMenu() { reset(); + updatePhase = 0; + drawItemLockout = false; } void SDMenu::resetState() { itemCount = countFiles(); + updatePhase = 0; + lastItemIndex = 0; + drawItemLockout = false; } // Count the number of files on the SD card @@ -1510,7 +1515,7 @@ void SDMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { return; } - const uint8_t MAX_FILE_LEN = LCD_SCREEN_WIDTH; + const uint8_t MAX_FILE_LEN = host::MAX_FILE_LEN; char fnbuf[MAX_FILE_LEN]; if ( !getFilename(index, fnbuf, MAX_FILE_LEN) ) { @@ -1518,10 +1523,51 @@ void SDMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { return; } + //Figure out length of filename + uint8_t filenameLength; + for (filenameLength = 0; (filenameLength < MAX_FILE_LEN) && (fnbuf[filenameLength] != 0); filenameLength++) ; + uint8_t idx; - for (idx = 0; (idx < MAX_FILE_LEN) && (fnbuf[idx] != 0); idx++) { - lcd.write(fnbuf[idx]); + uint8_t longFilenameOffset = 0; + uint8_t displayWidth = LCD_SCREEN_WIDTH - 1; + + //Support scrolling filenames that are longer than the lcd screen + if (filenameLength >= displayWidth) longFilenameOffset = updatePhase % (filenameLength - displayWidth + 1); + + for (idx = 0; (idx < displayWidth) && (fnbuf[longFilenameOffset + idx] != 0) && + ((longFilenameOffset + idx) < MAX_FILE_LEN); idx++) + lcd.write(fnbuf[longFilenameOffset + idx]); + + //Clear out the rest of the line + while ( idx < displayWidth ) { + lcd.write(' '); + idx ++; + } +} + +void SDMenu::update(LiquidCrystal& lcd, bool forceRedraw) { + + if (( ! forceRedraw ) && ( ! drawItemLockout )) { + //Redraw the last item if we have changed + if (((itemIndex/LCD_SCREEN_HEIGHT) == (lastDrawIndex/LCD_SCREEN_HEIGHT)) && + ( itemIndex != lastItemIndex )) { + lcd.setCursor(1,lastItemIndex % LCD_SCREEN_HEIGHT); + drawItem(lastItemIndex, lcd); + } + lastItemIndex = itemIndex; + + lcd.setCursor(1,itemIndex % LCD_SCREEN_HEIGHT); + drawItem(itemIndex, lcd); } + + Menu::update(lcd, forceRedraw); + + updatePhase ++; +} + +void SDMenu::notifyButtonPressed(ButtonArray::ButtonName button) { + updatePhase = 0; + Menu::notifyButtonPressed(button); } void SDMenu::handleSelect(uint8_t index) { @@ -1530,6 +1576,8 @@ void SDMenu::handleSelect(uint8_t index) { return; } + drawItemLockout = true; + char* buildName = host::getBuildName(); if ( !getFilename(index, buildName, host::MAX_FILE_LEN) ) { diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 7f905a1..a4c7cb6 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -160,10 +160,19 @@ public: class SDMenu: public Menu { +private: + uint8_t updatePhase; + uint8_t lastItemIndex; + bool drawItemLockout; public: SDMenu(); void resetState(); + + micros_t getUpdateRate() {return 500L * 1000L;} + void notifyButtonPressed(ButtonArray::ButtonName button); + + void update(LiquidCrystal& lcd, bool forceRedraw); protected: uint8_t countFiles(); From 98eb10698dd7dd81b2fefa9da3e719ca3ecdb02f Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 4 Jan 2012 15:52:52 -0700 Subject: [PATCH 16/61] Bug Fix: Fixed bug causing Volumes Name to be occasionally displayed in the list of files in the build menu. SDCard.cc was ignoring the attributes of the files returned and blindly assuming every entry was a file. It now ignores hidden, system, directories and volume names. --- firmware/src/Motherboard/SDCard.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/firmware/src/Motherboard/SDCard.cc b/firmware/src/Motherboard/SDCard.cc index b12b8c4..a2b2688 100644 --- a/firmware/src/Motherboard/SDCard.cc +++ b/firmware/src/Motherboard/SDCard.cc @@ -128,6 +128,9 @@ SdErrorCode directoryNextEntry(char* buffer, uint8_t bufsize) { uint8_t tries = 5; while (tries) { if (fat_read_dir(dd, &entry)) { + //Ignore non-file, system or hidden files + if ( entry.attributes & (FAT_ATTRIB_HIDDEN | FAT_ATTRIB_SYSTEM | FAT_ATTRIB_VOLUME | FAT_ATTRIB_DIR )) + continue; int i; for (i = 0; (i < bufsize-1) && entry.long_name[i] != 0; i++) { buffer[i] = entry.long_name[i]; From a4dd32d0a9bfa68af0fe9e707f05b30e4216b3f5 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 6 Jan 2012 12:12:40 -0700 Subject: [PATCH 17/61] Deleting .UART.cc.swp. Looks like an old vi tmp file left around by someone a long time ago. --- firmware/src/Extruder/boards/ecv34/.UART.cc.swp | Bin 12288 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 firmware/src/Extruder/boards/ecv34/.UART.cc.swp diff --git a/firmware/src/Extruder/boards/ecv34/.UART.cc.swp b/firmware/src/Extruder/boards/ecv34/.UART.cc.swp deleted file mode 100644 index e45c19186474391713801fbbe1642881ec3c44cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2Uu+vm9ml8qISAKlD|qG5?;3PNP+5}W@RV?D05Y5rfIsw-sQlm5bf zZy{`ko(|6*@yHmoHg2p-zc=8%u=W?Nz;9W_1MP=x;ag3`{dQo9*8Yu~Ry}9e(yi9n zrjph~fkc7#qd=&gg{9}}Hx{#**{d(mCq7<(Kckbri2{iNi2{iNi2{iNi2{iNi2{iN zPk{oue}Vl6aeM&01}?<_Y{te*Yml^vxcn3TLx4?Us82bhI8F&-q00X~%jZLk7T z;39bMB5Z@7f**qKfG>k4;2;YwgTFt^*k8e4z&qea;M?GvK!Y!UT~G&Wpa4Dvo(Dh1 zy9(b2kHH(@b?`;-Ij{*fz%?)f{x$R^F z@B{D`_#XH!cnk!vK?Qskd>UK<&w$^22s(prfp37Xg2&)Z&<7sKgEaUT*83me&)`qs z4C9Qz`dY7c+Leka1=O2su(1vz*8q zSkIc)V~aSB{b&^WoS%oe+Rk>V(8zD?6h5DdFYIEqwqt(fYQ-oNlNT>cVuSQL^t}~& zcs!h@=t$43PRfk2%u5(q(F@XWo@~=4^37BLX+@kyZlDG! z&|Hf&QLQTjGC#e(@-;`Gj`EG$gwMq5=+Zx^pm1Pym%E`z;Y~e(L^Bg|?T~ZZHvNoTI+u>zDbi+O=kL*31$|#d!IhWrmHY%m^WTw~_p+9#AdX@G z^U?miFI#X@_yI>DpW2yEoB}v+ut`UyHd4r}2wG;1MTgBIt4z#O>zbJ~PgY^^7RI1< zCil#apNrQh%B#h_%-rPmWazCdo5fDIUEIiJKeN13L-8Q>RUORDs{WE zQ!OI4Dq=$A83WBw)7?^SbEjUTT=^c|&Dl2W-CK!7!68m!UwD!8rbZ=BEWG~hVp z84J;Y6zgX4ri~|7U&1cBV%Ikn-c@}wA;;5^K01-ZKscd04->{p3O9!;^hx#24lsv$ z9ugj{gdY_&nxc4(wRIovSCym9d+4;TLa!a~8UpJ$89+D2vd0?FLmo31oWK}#G}bUf zFBo@xg8UrGVOGpM!{Qm@tjHbXSP(n5Y{H=y$3ZG4l9;*iBxzxta1Khx*fFLGND@4u obTutzvKjM?l56uGZS$e Date: Fri, 6 Jan 2012 12:28:59 -0700 Subject: [PATCH 18/61] Fixed timing of mood light state recognition when in Bot Status mode. --- firmware/src/Motherboard/Command.cc | 32 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index a3311a8..07eedfd 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -39,8 +39,8 @@ bool outstanding_tool_command = false; bool paused = false; uint16_t statusDivisor = 0; -uint32_t recentCommandClock = 0; -uint32_t recentCommandTime = 0; +volatile uint32_t recentCommandClock = 0; +volatile uint32_t recentCommandTime = 0; uint16_t getRemainingCapacity() { uint16_t sz; @@ -184,7 +184,6 @@ void runCommandSlice() { } if (paused) { return; } if (mode == HOMING) { - recentCommandTime = recentCommandClock; if (!steppers::isRunning()) { mode = READY; } else if (homing_timeout.hasElapsed()) { @@ -193,11 +192,9 @@ void runCommandSlice() { } } if (mode == MOVING) { - recentCommandTime = recentCommandClock; if (!steppers::isRunning()) { mode = READY; } } if (mode == DELAY) { - recentCommandTime = recentCommandClock; // check timers if (delay_timeout.hasElapsed()) { mode = READY; @@ -222,6 +219,7 @@ void runCommandSlice() { if (command_buffer.getLength() > 0) { uint8_t command = command_buffer[0]; if (command == HOST_CMD_QUEUE_POINT_ABS) { + recentCommandTime = recentCommandClock; // check for completion if (command_buffer.getLength() >= 17) { command_buffer.pop(); // remove the command code @@ -233,6 +231,7 @@ void runCommandSlice() { steppers::setTarget(Point(x,y,z),dda); } } else if (command == HOST_CMD_QUEUE_POINT_EXT) { + recentCommandTime = recentCommandClock; // check for completion if (command_buffer.getLength() >= 25) { command_buffer.pop(); // remove the command code @@ -246,6 +245,7 @@ void runCommandSlice() { steppers::setTarget(Point(x,y,z,a,b),dda); } } else if (command == HOST_CMD_QUEUE_POINT_NEW) { + recentCommandTime = recentCommandClock; // check for completion if (command_buffer.getLength() >= 26) { command_buffer.pop(); // remove the command code @@ -265,6 +265,7 @@ void runCommandSlice() { tool::setCurrentToolheadIndex(command_buffer.pop()); } } else if (command == HOST_CMD_ENABLE_AXES) { + recentCommandTime = recentCommandClock; if (command_buffer.getLength() >= 2) { command_buffer.pop(); // remove the command code uint8_t axes = command_buffer.pop(); @@ -444,14 +445,9 @@ void runCommandSlice() { #define STOCHASTIC_PERCENT(v, a, b) (((v - a) / (b - a)) * 100.0) #define MAX2(a,b) ((a >= b)?a:b) #define STATUS_DIVISOR_TIME_PER_STATUS_CHANGE 4000 -#define RECENT_COMMAND_TIMEOUT 4000 * 10 +#define RECENT_COMMAND_TIMEOUT 4000 * 200 void updateMoodStatus() { - MoodLightController moodLight = Motherboard::getBoard().getMoodLightController(); - - //If we're not set to the Bot Status Script, then there's no need to check anything - if ( moodLight.getLastScriptPlayed() != 0 ) return; - //Implement a divisor so we don't get called on every turn, we don't //want to overload the interrupt loop when we don't need frequent changes statusDivisor ++; @@ -459,6 +455,12 @@ void updateMoodStatus() { if ( statusDivisor < STATUS_DIVISOR_TIME_PER_STATUS_CHANGE ) return; statusDivisor = 0; + MoodLightController moodLight = Motherboard::getBoard().getMoodLightController(); + + //If we're not set to the Bot Status Script, then there's no need to check anything + if ( moodLight.getLastScriptPlayed() != 0 ) return; + + //Certain states don't require us to check as often, //save some CPU cycles @@ -534,14 +536,18 @@ void updateMoodStatus() { ( recentCommandTime >= (recentCommandClock - RECENT_COMMAND_TIMEOUT ))) { mlStatus = MOOD_LIGHT_STATUS_PRINTING; } else { - mlStatus = MOOD_LIGHT_STATUS_HEATING; + //We can't go from printing back to heating + if ( lastMlStatus != MOOD_LIGHT_STATUS_PRINTING ) mlStatus = MOOD_LIGHT_STATUS_HEATING; + else mlStatus = MOOD_LIGHT_STATUS_PRINTING; } } else { //If we're cooling and tool and platform are 0%, then we're not cooling anymore if (( percentHotTool <= 0.0 ) && ( percentHotPlatform <= 0.0 )) { mlStatus = MOOD_LIGHT_STATUS_IDLE; } else { - mlStatus = MOOD_LIGHT_STATUS_COOLING; + //We can't go from idle back to cooling + if ( lastMlStatus != MOOD_LIGHT_STATUS_IDLE ) mlStatus = MOOD_LIGHT_STATUS_COOLING; + else mlStatus = MOOD_LIGHT_STATUS_IDLE; } } From abb44e734d86101aae762255f9aab0a603474863 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 6 Jan 2012 13:12:25 -0700 Subject: [PATCH 19/61] Added Z Position (in mm) to build menu. Hitting the OK Button now stop the rotation on the build menu. --- firmware/src/shared/Menu.cc | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index b4e5b54..bd7accb 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -968,6 +968,7 @@ void MonitorMode::reset() { lastElapsedSeconds = 0.0; } + void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar extruder_temp[] = "Tool: ---/---C"; const static PROGMEM prog_uchar platform_temp[] = "Bed: ---/---C"; @@ -977,6 +978,8 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar time_left_calc[] = " calc.."; const static PROGMEM prog_uchar time_left_1min[] = " <1min"; const static PROGMEM prog_uchar time_left_none[] = " none"; + const static PROGMEM prog_uchar zpos[] = "ZPos: "; + const static PROGMEM prog_uchar zpos_mm[] = "mm"; char buf[17]; if (forceRedraw) { @@ -1055,6 +1058,11 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { float secs; + //Holding the zero button stops rotation + if ( ! interface::isButtonPressed(ButtonArray::OK) ) buildTimePhase ++; + + if ( buildTimePhase >= 4 ) buildTimePhase = 0; + switch (buildTimePhase) { case 0: lcd.setCursor(0,1); @@ -1110,10 +1118,20 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { } } break; - } + case 3: + lcd.setCursor(0,1); + lcd.writeFromPgmspace(zpos); + lcd.setCursor(6,1); - buildTimePhase ++; - if ( buildTimePhase >= 3 ) buildTimePhase = 0; + Point position = steppers::getPosition(); + + //Divide by 200m because there are 200 steps per mm + lcd.writeFloat((float)position[2] / 200.0, 3); + + lcd.writeFromPgmspace(zpos_mm); + break; + } + break; } From 7a65b65276dfeba44713622eb47dfaa1b7953f15 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 6 Jan 2012 17:13:36 -0700 Subject: [PATCH 20/61] Added "Pause Build" to Build From SD. When paused, Z moves up 2mm (to avoid mushing the plastic as in ReplicatorG), then you can jog and extrude as necessary to change filament. When exiting pause mode, everything returns back to where it was (requires consistent mechanical X/Y/Z positioning). --- firmware/src/Motherboard/Steppers.cc | 4 + firmware/src/Motherboard/Steppers.hh | 3 + .../Motherboard/boards/mb24/Motherboard.cc | 3 +- firmware/src/shared/Menu.cc | 194 +++++++++++++++++- firmware/src/shared/Menu.hh | 22 ++ 5 files changed, 215 insertions(+), 11 deletions(-) diff --git a/firmware/src/Motherboard/Steppers.cc b/firmware/src/Motherboard/Steppers.cc index dce666f..f6212d4 100644 --- a/firmware/src/Motherboard/Steppers.cc +++ b/firmware/src/Motherboard/Steppers.cc @@ -35,6 +35,10 @@ bool isRunning() { return is_running || is_homing; } +bool isHoming() { + return is_homing; +} + //public: void init(Motherboard& motherboard) { is_running = false; diff --git a/firmware/src/Motherboard/Steppers.hh b/firmware/src/Motherboard/Steppers.hh index 92f6895..84f8303 100644 --- a/firmware/src/Motherboard/Steppers.hh +++ b/firmware/src/Motherboard/Steppers.hh @@ -38,6 +38,9 @@ namespace steppers { /// otherwise. bool isRunning(); + /// Returns true if the stepper subsystem is homing + bool isHoming(); + /// Abort the current motion and set the stepper subsystem to /// the not-running state. void abort(); diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index c17b460..49140fc 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -205,8 +205,7 @@ void Motherboard::doInterrupt() { seconds += 1; countupMicros -= 1000000L; } - // Do not move steppers if the board is in a paused state - if (command::isPaused()) return; + steppers::doInterrupt(); } diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index bd7accb..381a3da 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -33,6 +33,7 @@ int16_t overrideExtrudeSeconds = 0; +Point pausedPosition; void strcat(char *buf, const char* str) @@ -1303,35 +1304,57 @@ void Menu::notifyButtonPressed(ButtonArray::ButtonName button) { CancelBuildMenu::CancelBuildMenu() { itemCount = MAX_ITEMS_PER_SCREEN; reset(); + pauseDisabled = false; + if ( steppers::isHoming() ) pauseDisabled = true; } void CancelBuildMenu::resetState() { - itemIndex = 2; - firstItemIndex = 2; + pauseDisabled = false; + if ( steppers::isHoming() ) pauseDisabled = true; + + if ( pauseDisabled ) itemIndex = 2; + else itemIndex = 1; + + firstItemIndex = itemIndex; } void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar cancel[] = "Cancel Build?"; - const static PROGMEM prog_uchar yes[] = "Yes"; - const static PROGMEM prog_uchar no[] = "No"; + const static PROGMEM prog_uchar stop[] = "Stop Build?"; + const static PROGMEM prog_uchar pause[] = "Pause "; + const static PROGMEM prog_uchar abort[] = "Abort Print"; + const static PROGMEM prog_uchar cancel[] = "Cancel "; + + if ( steppers::isHoming() ) pauseDisabled = true; switch (index) { case 0: - lcd.writeFromPgmspace(cancel); + lcd.writeFromPgmspace(stop); break; case 1: + if ( ! pauseDisabled ) { + lcd.writeFromPgmspace(pause); + } break; case 2: - lcd.writeFromPgmspace(yes); + lcd.writeFromPgmspace(abort); break; case 3: - lcd.writeFromPgmspace(no); + lcd.writeFromPgmspace(cancel); break; } } void CancelBuildMenu::handleSelect(uint8_t index) { + int32_t interval = 2000; + switch (index) { + case 1: + // Pause + if ( ! pauseDisabled ) { + command::pause(true); + interface::pushScreen(&pauseMode); + } + break; case 2: // Cancel build, returning to whatever menu came before monitor mode. // TODO: Cancel build. @@ -1339,7 +1362,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { host::stopBuild(); break; case 3: - // Don't cancel, just close dialog. + // Don't cancel print, just close dialog. interface::popScreen(); break; } @@ -2012,4 +2035,157 @@ void TestEndStopsMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } +void PauseMode::reset() { + pauseState = 0; + lastDirectionButtonPressed = (ButtonArray::ButtonName)0; +} + +void PauseMode::jog(ButtonArray::ButtonName direction) { + uint8_t steps = 50; + bool extrude = false; + int32_t interval = 1000; + Point position = steppers::getPosition(); + + switch(direction) { + case ButtonArray::XMINUS: + position[0] -= steps; + break; + case ButtonArray::XPLUS: + position[0] += steps; + break; + case ButtonArray::YMINUS: + position[1] -= steps; + break; + case ButtonArray::YPLUS: + position[1] += steps; + break; + case ButtonArray::ZMINUS: + position[2] -= steps; + break; + case ButtonArray::ZPLUS: + position[2] += steps; + break; + case ButtonArray::OK: + case ButtonArray::ZERO: + float rpm = (float)eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19) / 10.0; + + //60 * 1000000 = # uS in a minute + //200 * 8 = 200 steps per revolution * 1/8 stepping + interval = (int32_t)(60L * 1000000L) / (int32_t)((float)(200 * 8) * rpm); + int16_t stepsPerSecond = (int16_t)((200.0 * 8.0 * rpm) / 60.0); + + //Handle reverse + if ( direction == ButtonArray::OK ) stepsPerSecond *= -1; + + //Extrude for 0.5 seconds + position[3] += 0.5 * stepsPerSecond; + break; + } + + lastDirectionButtonPressed = direction; + + steppers::setTarget(position, interval); +} + + +void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar waitForCurrentCommand[] = "Entering pause.."; + const static PROGMEM prog_uchar movingZ[] = "Moving Z up 2mm "; + const static PROGMEM prog_uchar leavingPaused[] = "Leaving pause.. "; + const static PROGMEM prog_uchar paused1[] = "Paused: "; + const static PROGMEM prog_uchar paused2[] = " Y+ Z+"; + const static PROGMEM prog_uchar paused3[] = "X- Rev X+ (Fwd)"; + const static PROGMEM prog_uchar paused4[] = " Y- Z-"; + + int32_t interval = 2000; + Point newPosition = pausedPosition; + + if (forceRedraw) lcd.clear(); + + lcd.setCursor(0,0); + + switch (pauseState) { + case 0: //Entered pause, waiting for steppers to finish last command + lcd.writeFromPgmspace(waitForCurrentCommand); + + if ( ! steppers::isRunning()) pauseState ++; + break; + + case 1: //Last command finished, record current position and move + //Z away from build + lcd.writeFromPgmspace(movingZ); + + pausedPosition = steppers::getPosition(); + newPosition = pausedPosition; + newPosition[2] += 2 * 200; //200 because of the number of steps per mm + steppers::setTarget(newPosition, interval); + + pauseState ++; + break; + + case 2: //Wait for the Z move up to complete + lcd.writeFromPgmspace(movingZ); + if ( ! steppers::isRunning()) { + pauseState ++; + + //We write this here to avoid tieing up the processor + //in the next state + lcd.clear(); + lcd.writeFromPgmspace(paused1); + lcd.setCursor(0,1); + lcd.writeFromPgmspace(paused2); + lcd.setCursor(0,2); + lcd.writeFromPgmspace(paused3); + lcd.setCursor(0,3); + lcd.writeFromPgmspace(paused4); + } + break; + + case 3: //We're now paused + break; + + case 4: //Leaving paused, wait for any steppers to finish + lcd.clear(); + lcd.writeFromPgmspace(leavingPaused); + if ( ! steppers::isRunning()) pauseState ++; + break; + + case 5: //Return to original position + lcd.writeFromPgmspace(leavingPaused); + + //The extruders may have moved, so it doesn't make sense + //to go back to the old position, or we'll eject the filament + newPosition = steppers::getPosition(); + pausedPosition[3] = newPosition[3]; + pausedPosition[4] = newPosition[4]; + + steppers::setTarget(pausedPosition, interval); + pauseState ++; + break; + + case 6: //Wait for return to original position + lcd.writeFromPgmspace(leavingPaused); + if ( ! steppers::isRunning()) { + pauseState = 0; + interface::popScreen(); + interface::popScreen(); + command::pause(false); + } + break; + } + + if ( lastDirectionButtonPressed ) { + if (interface::isButtonPressed(lastDirectionButtonPressed)) + jog(lastDirectionButtonPressed); + else lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + } +} + +void PauseMode::notifyButtonPressed(ButtonArray::ButtonName button) { + if ( button == ButtonArray::CANCEL ) { + if ( pauseState == 3 ) pauseState ++; + } + else jog(button); +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index a4c7cb6..e07c7a0 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -186,6 +186,25 @@ protected: }; +class PauseMode: public Screen { +private: + ButtonArray::ButtonName lastDirectionButtonPressed; + + void jog(ButtonArray::ButtonName direction); + + uint8_t pauseState; + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + + class CancelBuildMenu: public Menu { public: CancelBuildMenu(); @@ -195,6 +214,9 @@ protected: void drawItem(uint8_t index, LiquidCrystal& lcd); void handleSelect(uint8_t index); +private: + PauseMode pauseMode; + bool pauseDisabled; }; From 4c08c0d3c5b534a59caca7048ad43a7c62e55a15 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Mon, 9 Jan 2012 17:05:44 -0700 Subject: [PATCH 21/61] Added a "view" mode to the Jog Menu. You can choose if you the X-/X+/Y-/Y+ buttons move the platform from the point of view of the model, or from the point of view of the user. "Model" matches Control Panel in RepG and the original Jog Mode, this is the default. This setting is remembered. Also the "Jog Amount" is now remembered in Eeprom. --- firmware/src/Motherboard/EepromMap.cc | 1 + firmware/src/Motherboard/EepromMap.hh | 4 + firmware/src/shared/Menu.cc | 104 ++++++++++++++++++++++---- firmware/src/shared/Menu.hh | 16 +++- 4 files changed, 110 insertions(+), 15 deletions(-) diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index a891823..bf74275 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -39,6 +39,7 @@ void setDefaults() { eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_RED,255); eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_GREEN,255); eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_BLUE,255); + eeprom_write_byte((uint8_t*)eeprom::JOG_MODE_SETTINGS,0); } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index 4f42c37..edb42a2 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -68,6 +68,10 @@ const static uint16_t MOOD_LIGHT_CUSTOM_RED = 0x0086; const static uint16_t MOOD_LIGHT_CUSTOM_GREEN = 0x0087; const static uint16_t MOOD_LIGHT_CUSTOM_BLUE = 0x0088; +//Bit 1 is Model mode or user view mode (user view mode = bit set) +//Bit 2-4 are the jog mode distance 0 = short, 1 = long, 2 = cont +const static uint16_t JOG_MODE_SETTINGS = 0x0089; + /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 381a3da..61a8b1e 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -224,23 +224,86 @@ void SplashScreen::notifyButtonPressed(ButtonArray::ButtonName button) { void SplashScreen::reset() { } +UserViewMenu::UserViewMenu() { + itemCount = 4; + reset(); +} + +void UserViewMenu::resetState() { + uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0); + + if ( jogModeSettings & 0x01 ) itemIndex = 3; + else itemIndex = 2; + + firstItemIndex = 2; +} + +void UserViewMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg[] = "X/Y Direction:"; + const static PROGMEM prog_uchar model[]= "Model View"; + const static PROGMEM prog_uchar user[] = "User View"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg); + break; + case 1: + break; + case 2: + lcd.writeFromPgmspace(model); + break; + case 3: + lcd.writeFromPgmspace(user); + break; + } +} + +void UserViewMenu::handleSelect(uint8_t index) { + uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0); + + switch (index) { + case 2: + // Model View + eeprom_write_byte((uint8_t *)eeprom::JOG_MODE_SETTINGS, (jogModeSettings & (uint8_t)0xFE)); + interface::popScreen(); + break; + case 3: + // User View + eeprom_write_byte((uint8_t *)eeprom::JOG_MODE_SETTINGS, (jogModeSettings | (uint8_t)0x01)); + interface::popScreen(); + break; + } +} + void JogMode::reset() { - jogDistance = DISTANCE_SHORT; + uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0); + + jogDistance = (enum distance_t)((jogModeSettings >> 1 ) & 0x07); + if ( jogDistance > DISTANCE_CONT ) jogDistance = DISTANCE_SHORT; + distanceChanged = false; lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + + userViewMode = jogModeSettings & 0x01; + userViewModeChanged = false; } void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar jog1[] = "Jog mode: "; - const static PROGMEM prog_uchar jog2[] = " Y+ Z+"; - const static PROGMEM prog_uchar jog3[] = "X- X+ (mode)"; - const static PROGMEM prog_uchar jog4[] = " Y- Z-"; + const static PROGMEM prog_uchar jog1[] = "Jog mode: "; + const static PROGMEM prog_uchar jog2[] = " Y+ Z+"; + const static PROGMEM prog_uchar jog3[] = "X- V X+ (mode)"; + const static PROGMEM prog_uchar jog4[] = " Y- Z-"; + const static PROGMEM prog_uchar jog2_user[] = " Y Z+"; + const static PROGMEM prog_uchar jog3_user[] = "X V X (mode)"; + const static PROGMEM prog_uchar jog4_user[] = " Y Z-"; const static PROGMEM prog_uchar distanceShort[] = "SHORT"; const static PROGMEM prog_uchar distanceLong[] = "LONG"; const static PROGMEM prog_uchar distanceCont[] = "CONT"; - if (forceRedraw || distanceChanged) { + if ( userViewModeChanged ) userViewMode = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0) & 0x01; + + if (forceRedraw || distanceChanged || userViewModeChanged) { lcd.clear(); lcd.setCursor(0,0); lcd.writeFromPgmspace(jog1); @@ -258,15 +321,19 @@ void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { } lcd.setCursor(0,1); - lcd.writeFromPgmspace(jog2); + if ( userViewMode ) lcd.writeFromPgmspace(jog2_user); + else lcd.writeFromPgmspace(jog2); lcd.setCursor(0,2); - lcd.writeFromPgmspace(jog3); + if ( userViewMode ) lcd.writeFromPgmspace(jog3_user); + else lcd.writeFromPgmspace(jog3); lcd.setCursor(0,3); - lcd.writeFromPgmspace(jog4); + if ( userViewMode ) lcd.writeFromPgmspace(jog4_user); + else lcd.writeFromPgmspace(jog4); distanceChanged = false; + userViewModeChanged = false; } if ( jogDistance == DISTANCE_CONT ) { @@ -282,7 +349,7 @@ void JogMode::jog(ButtonArray::ButtonName direction) { Point position = steppers::getPosition(); int32_t interval = 2000; - uint8_t steps; + int32_t steps; if ( jogDistance == DISTANCE_CONT ) interval = 1000; @@ -298,18 +365,23 @@ void JogMode::jog(ButtonArray::ButtonName direction) { break; } + //Reverse direction of X and Y if we're in User View Mode and + //not model mode + uint32_t vMode = 1; + if ( userViewMode ) vMode = -1;; + switch(direction) { case ButtonArray::XMINUS: - position[0] -= steps; + position[0] -= vMode * steps; break; case ButtonArray::XPLUS: - position[0] += steps; + position[0] += vMode * steps; break; case ButtonArray::YMINUS: - position[1] -= steps; + position[1] -= vMode * steps; break; case ButtonArray::YPLUS: - position[1] += steps; + position[1] += vMode * steps; break; case ButtonArray::ZMINUS: position[2] -= steps; @@ -328,6 +400,9 @@ void JogMode::jog(ButtonArray::ButtonName direction) { void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { switch (button) { case ButtonArray::ZERO: + userViewModeChanged = true; + interface::pushScreen(&userViewMenu); + break; case ButtonArray::OK: switch(jogDistance) { @@ -342,6 +417,7 @@ void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { break; } distanceChanged = true; + eeprom_write_byte((uint8_t *)eeprom::JOG_MODE_SETTINGS, userViewMode | (jogDistance << 1)); break; case ButtonArray::YMINUS: case ButtonArray::ZMINUS: diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index e07c7a0..64d2767 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -89,17 +89,31 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class UserViewMenu: public Menu { +public: + UserViewMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; class JogMode: public Screen { private: enum distance_t { - DISTANCE_SHORT, + DISTANCE_SHORT = 0, DISTANCE_LONG, DISTANCE_CONT, }; + UserViewMenu userViewMenu; + distance_t jogDistance; bool distanceChanged; + bool userViewMode; + bool userViewModeChanged; ButtonArray::ButtonName lastDirectionButtonPressed; void jog(ButtonArray::ButtonName direction); From 2abd1445c18736d0da9d08afcb267137d99f6b9a Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 11 Jan 2012 14:30:02 -0700 Subject: [PATCH 22/61] Fixed bug causing print build to start before HBP reached temperature. --- firmware/src/Motherboard/Command.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index 07eedfd..d91e67e 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -129,7 +129,7 @@ bool querySlaveCmd(uint8_t cmd, uint8_t *result) { InPacket& in = tool::getInPacket(); out.reset(); out.append8(tool::getCurrentToolheadIndex()); - out.append8(SLAVE_CMD_GET_TOOL_STATUS); + out.append8(cmd); tool::startTransaction(); // WHILE: bounded by timeout in runToolSlice From e70a36f74d7734db3089dc481d8b6a0fd02b8328 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 11 Jan 2012 18:17:31 -0700 Subject: [PATCH 23/61] Added "Pause at ZPos" to the cancel menu when building from SD Card. The enables the user to preprogram in a set Z Position (in mm), where the build will pause. Multiple pauses can be setup, by setting up a new pause after the current pause has completed. A value of zero cancels the current pause. A build that will pause is indicated by an asterisk on the build monitor screen. Your bot should not be left unattended when this feature is used, because the Extruder and HBP will remain at temperature. --- firmware/src/Motherboard/Command.cc | 18 +++++ firmware/src/Motherboard/Command.hh | 8 ++ firmware/src/shared/Menu.cc | 119 +++++++++++++++++++++++++--- firmware/src/shared/Menu.hh | 25 +++++- 4 files changed, 157 insertions(+), 13 deletions(-) diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index d91e67e..c352a2a 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -41,6 +41,7 @@ bool paused = false; uint16_t statusDivisor = 0; volatile uint32_t recentCommandClock = 0; volatile uint32_t recentCommandTime = 0; +volatile float pauseZPos = 0.0; uint16_t getRemainingCapacity() { uint16_t sz; @@ -58,6 +59,14 @@ bool isPaused() { return paused; } +void pauseAtZPos(float zpos) { + pauseZPos = zpos; +} + +float getPauseAtZPos() { + return pauseZPos; +} + bool isEmpty() { return command_buffer.isEmpty(); } @@ -112,6 +121,7 @@ Timeout homing_timeout; Timeout tool_wait_timeout; void reset() { + pauseAtZPos(0.0); command_buffer.reset(); mode = READY; } @@ -183,6 +193,14 @@ void runCommandSlice() { } } if (paused) { return; } + + //If PauseAtZPos is enabled, pause when we reach zpos + //0.005 because of float point rounding errors, we want + //to make sure we stop at the correct place + if (( pauseZPos != 0.0) && ( ! isPaused() ) && + (((float)steppers::getPosition()[2] / 200.0) >= (pauseZPos - 0.005) )) + pause(true); + if (mode == HOMING) { if (!steppers::isRunning()) { mode = READY; diff --git a/firmware/src/Motherboard/Command.hh b/firmware/src/Motherboard/Command.hh index 66209af..e8f9f2f 100644 --- a/firmware/src/Motherboard/Command.hh +++ b/firmware/src/Motherboard/Command.hh @@ -41,6 +41,14 @@ void pause(bool pause); /// \return True if it is disabled, false if it is enabled. bool isPaused(); +/// \Pause at >= a Z Position provded in mm +/// 0 cancels pauseAtZPos +void pauseAtZPos(float zpos); + +/// Get the current pauseAtZPos position +/// \return the z position set for pausing, otherwise 0 +float getPauseAtZPos(); + /// Check the remaining capacity of the command buffer /// \return Amount of space left in the buffer, in bytes uint16_t getRemainingCapacity(); diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 61a8b1e..ebde1f0 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1043,6 +1043,8 @@ void MonitorMode::reset() { buildComplete = false; extruderStartSeconds = 0.0; lastElapsedSeconds = 0.0; + pausePushLockout = false; + pauseMode.autoPause = false; } @@ -1059,6 +1061,15 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar zpos_mm[] = "mm"; char buf[17]; + if ( command::isPaused() ) { + if ( ! pausePushLockout ) { + pausePushLockout = true; + pauseMode.autoPause = true; + interface::pushScreen(&pauseMode); + return; + } + } else pausePushLockout = false; + if (forceRedraw) { lcd.clear(); lcd.setCursor(0,0); @@ -1082,8 +1093,11 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,3); lcd.writeFromPgmspace(platform_temp); - } + lcd.setCursor(15,3); + if ( command::getPauseAtZPos() == 0.0 ) lcd.write(' '); + else lcd.write('*'); + } OutPacket responsePacket; @@ -1127,6 +1141,10 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { } else { lcd.writeString("XXX"); } + + lcd.setCursor(15,3); + if ( command::getPauseAtZPos() == 0.0 ) lcd.write(' '); + else lcd.write('*'); break; case 4: enum host::HostState hostState = host::getHostState(); @@ -1378,27 +1396,35 @@ void Menu::notifyButtonPressed(ButtonArray::ButtonName button) { CancelBuildMenu::CancelBuildMenu() { - itemCount = MAX_ITEMS_PER_SCREEN; + pauseMode.autoPause = false; + itemCount = 5; reset(); pauseDisabled = false; if ( steppers::isHoming() ) pauseDisabled = true; } void CancelBuildMenu::resetState() { + pauseMode.autoPause = false; pauseDisabled = false; if ( steppers::isHoming() ) pauseDisabled = true; - if ( pauseDisabled ) itemIndex = 2; - else itemIndex = 1; + if ( pauseDisabled ) { + itemIndex = 2; + itemCount = 4; + } else { + itemIndex = 1; + itemCount = 5; + } firstItemIndex = itemIndex; } void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar stop[] = "Stop Build?"; - const static PROGMEM prog_uchar pause[] = "Pause "; - const static PROGMEM prog_uchar abort[] = "Abort Print"; - const static PROGMEM prog_uchar cancel[] = "Cancel "; + const static PROGMEM prog_uchar pause[] = "Pause "; + const static PROGMEM prog_uchar abort[] = "Abort Print "; + const static PROGMEM prog_uchar cancel[] = "Cancel "; + const static PROGMEM prog_uchar pauseZ[] = "Pause at ZPos"; if ( steppers::isHoming() ) pauseDisabled = true; @@ -1407,9 +1433,7 @@ void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(stop); break; case 1: - if ( ! pauseDisabled ) { - lcd.writeFromPgmspace(pause); - } + if ( ! pauseDisabled ) lcd.writeFromPgmspace(pause); break; case 2: lcd.writeFromPgmspace(abort); @@ -1417,6 +1441,9 @@ void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { case 3: lcd.writeFromPgmspace(cancel); break; + case 4: + if ( ! pauseDisabled ) lcd.writeFromPgmspace(pauseZ); + break; } } @@ -1428,6 +1455,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { // Pause if ( ! pauseDisabled ) { command::pause(true); + pauseMode.autoPause = false; interface::pushScreen(&pauseMode); } break; @@ -1441,6 +1469,9 @@ void CancelBuildMenu::handleSelect(uint8_t index) { // Don't cancel print, just close dialog. interface::popScreen(); break; + case 4: + if ( ! pauseDisabled ) interface::pushScreen(&pauseAtZPosScreen); + break; } } @@ -2221,6 +2252,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { break; case 4: //Leaving paused, wait for any steppers to finish + if ( autoPause ) command::pauseAtZPos(0.0); lcd.clear(); lcd.writeFromPgmspace(leavingPaused); if ( ! steppers::isRunning()) pauseState ++; @@ -2244,8 +2276,8 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( ! steppers::isRunning()) { pauseState = 0; interface::popScreen(); - interface::popScreen(); command::pause(false); + if ( ! autoPause ) interface::popScreen(); } break; } @@ -2264,4 +2296,69 @@ void PauseMode::notifyButtonPressed(ButtonArray::ButtonName button) { else jog(button); } +void PauseAtZPosScreen::reset() { + float currentPause = command::getPauseAtZPos(); + if ( currentPause == 0.0 ) { + Point position = steppers::getPosition(); + pauseAtZPos = (float)position[2] / 200.0; + } else pauseAtZPos = currentPause; +} + +void PauseAtZPosScreen::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Pause at ZPos:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar mm[] = "mm "; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(0,1); + lcd.writeFloat((float)pauseAtZPos, 3); + lcd.writeFromPgmspace(mm); +} + +void PauseAtZPosScreen::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::OK: + case ButtonArray::ZERO: + //Set the pause + command::pauseAtZPos(pauseAtZPos); + case ButtonArray::CANCEL: + interface::popScreen(); + interface::popScreen(); + break; + case ButtonArray::ZPLUS: + // increment more + if (pauseAtZPos <= 250) pauseAtZPos += 1.0; + break; + case ButtonArray::ZMINUS: + // decrement more + if (pauseAtZPos >= 1.0) pauseAtZPos -= 1.0; + else pauseAtZPos = 0.0; + break; + case ButtonArray::YPLUS: + // increment less + if (pauseAtZPos <= 254) pauseAtZPos += 0.05; + break; + case ButtonArray::YMINUS: + // decrement less + if (pauseAtZPos >= 0.05) pauseAtZPos -= 0.05; + else pauseAtZPos = 0.0; + break; + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } + + if ( pauseAtZPos < 0.001 ) pauseAtZPos = 0.0; +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 64d2767..edb53ca 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -208,6 +208,23 @@ private: uint8_t pauseState; +public: + bool autoPause; + + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + + +class PauseAtZPosScreen: public Screen { +private: + float pauseAtZPos; + public: micros_t getUpdateRate() {return 50L * 1000L;} @@ -224,13 +241,15 @@ public: CancelBuildMenu(); void resetState(); + protected: void drawItem(uint8_t index, LiquidCrystal& lcd); void handleSelect(uint8_t index); private: - PauseMode pauseMode; - bool pauseDisabled; + PauseMode pauseMode; + bool pauseDisabled; + PauseAtZPosScreen pauseAtZPosScreen; }; @@ -243,6 +262,8 @@ private: float lastElapsedSeconds; float extruderStartSeconds; bool buildComplete; //For solving floating point rounding issues + PauseMode pauseMode; + bool pausePushLockout; public: micros_t getUpdateRate() {return 500L * 1000L;} From e789b6abf2d424e86f680684d83040cc98f5614c Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 13 Jan 2012 17:32:21 -0700 Subject: [PATCH 24/61] Made the cancel menu more user friendly when building from SD Card. --- firmware/src/shared/Menu.cc | 97 ++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index ebde1f0..eac333d 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1400,13 +1400,13 @@ CancelBuildMenu::CancelBuildMenu() { itemCount = 5; reset(); pauseDisabled = false; - if ( steppers::isHoming() ) pauseDisabled = true; + if (( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; } void CancelBuildMenu::resetState() { pauseMode.autoPause = false; pauseDisabled = false; - if ( steppers::isHoming() ) pauseDisabled = true; + if (( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; if ( pauseDisabled ) { itemIndex = 2; @@ -1420,59 +1420,76 @@ void CancelBuildMenu::resetState() { } void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar stop[] = "Stop Build?"; - const static PROGMEM prog_uchar pause[] = "Pause "; - const static PROGMEM prog_uchar abort[] = "Abort Print "; - const static PROGMEM prog_uchar cancel[] = "Cancel "; - const static PROGMEM prog_uchar pauseZ[] = "Pause at ZPos"; + const static PROGMEM prog_uchar choose[] = "Please Choose:"; + const static PROGMEM prog_uchar abort[] = "Abort Print "; + const static PROGMEM prog_uchar pauseZ[] = "Pause at ZPos "; + const static PROGMEM prog_uchar pause[] = "Pause "; + const static PROGMEM prog_uchar back[] = "Continue Build"; - if ( steppers::isHoming() ) pauseDisabled = true; + if (( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; - switch (index) { - case 0: - lcd.writeFromPgmspace(stop); - break; - case 1: - if ( ! pauseDisabled ) lcd.writeFromPgmspace(pause); - break; - case 2: - lcd.writeFromPgmspace(abort); - break; - case 3: - lcd.writeFromPgmspace(cancel); - break; - case 4: - if ( ! pauseDisabled ) lcd.writeFromPgmspace(pauseZ); - break; + //Implement variable length menu + uint8_t lind = 0; + + if ( index == lind ) lcd.writeFromPgmspace(choose); + lind ++; + + if ( pauseDisabled ) lind ++; + + if ( index == lind) lcd.writeFromPgmspace(abort); + lind ++; + + if ( ! pauseDisabled ) { + if ( index == lind ) lcd.writeFromPgmspace(pauseZ); + lind ++; + } + + if ( ! pauseDisabled ) { + if ( index == lind ) lcd.writeFromPgmspace(pause); + lind ++; } + + if ( index == lind ) lcd.writeFromPgmspace(back); + lind ++; } void CancelBuildMenu::handleSelect(uint8_t index) { int32_t interval = 2000; - switch (index) { - case 1: - // Pause - if ( ! pauseDisabled ) { - command::pause(true); - pauseMode.autoPause = false; - interface::pushScreen(&pauseMode); - } - break; - case 2: + //Implement variable length menu + uint8_t lind = 0; + + if ( pauseDisabled ) lind ++; + + lind ++; + + if ( index == lind) { // Cancel build, returning to whatever menu came before monitor mode. // TODO: Cancel build. interface::popScreen(); host::stopBuild(); - break; - case 3: + } + lind ++; + + if ( ! pauseDisabled ) { + if ( index == lind ) interface::pushScreen(&pauseAtZPosScreen); + lind ++; + } + + if ( ! pauseDisabled ) { + if ( index == lind ) { + command::pause(true); + pauseMode.autoPause = false; + interface::pushScreen(&pauseMode); + } + lind ++; + } + + if ( index == lind ) { // Don't cancel print, just close dialog. interface::popScreen(); - break; - case 4: - if ( ! pauseDisabled ) interface::pushScreen(&pauseAtZPosScreen); - break; } + lind ++; } From e56df8ebb02831d034a2805bc7718d139701342c Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 13 Jan 2012 22:50:17 -0700 Subject: [PATCH 25/61] Added "Advance ABP" menu item --- firmware/src/Motherboard/ExtruderControl.cc | 3 +- firmware/src/Motherboard/ExtruderControl.hh | 3 +- firmware/src/shared/Menu.cc | 98 ++++++++++++++++----- firmware/src/shared/Menu.hh | 15 ++++ 4 files changed, 97 insertions(+), 22 deletions(-) diff --git a/firmware/src/Motherboard/ExtruderControl.cc b/firmware/src/Motherboard/ExtruderControl.cc index d0269b0..a10c6d6 100755 --- a/firmware/src/Motherboard/ExtruderControl.cc +++ b/firmware/src/Motherboard/ExtruderControl.cc @@ -45,7 +45,8 @@ bool extruderControl(uint8_t command, enum extruderCommandType cmdType, // second is the out.append8(0); out.append8(command); - if ( cmdType == EXTDR_CMD_SET ) out.append16(val); + if ( cmdType == EXTDR_CMD_SET ) out.append16(val); + if ( cmdType == EXTDR_CMD_SET8 ) out.append8((uint8_t)val); // Timeouts are handled inside the toolslice code; there's no need // to check for timeouts on this loop. diff --git a/firmware/src/Motherboard/ExtruderControl.hh b/firmware/src/Motherboard/ExtruderControl.hh index a6c8f3f..a700256 100755 --- a/firmware/src/Motherboard/ExtruderControl.hh +++ b/firmware/src/Motherboard/ExtruderControl.hh @@ -27,7 +27,8 @@ enum extruderCommandType { EXTDR_CMD_GET, - EXTDR_CMD_SET + EXTDR_CMD_SET, //16 bit + EXTDR_CMD_SET8 //8 bit }; diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index eac333d..c1fd57d 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1494,22 +1494,23 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 11; + itemCount = 12; reset(); } void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar monitor[] = "Monitor"; - const static PROGMEM prog_uchar build[] = "Build from SD"; - const static PROGMEM prog_uchar jog[] = "Jog"; - const static PROGMEM prog_uchar preheat[] = "Preheat"; - const static PROGMEM prog_uchar extruder[] = "Extrude"; - const static PROGMEM prog_uchar homeAxis[] = "Home Axis"; - const static PROGMEM prog_uchar steppersS[]= "Steppers"; - const static PROGMEM prog_uchar moodlight[]= "Mood Light"; - const static PROGMEM prog_uchar endStops[] = "Test End Stops"; - const static PROGMEM prog_uchar versions[] = "Version"; - const static PROGMEM prog_uchar snake[] = "Snake Game"; + const static PROGMEM prog_uchar monitor[] = "Monitor"; + const static PROGMEM prog_uchar build[] = "Build from SD"; + const static PROGMEM prog_uchar jog[] = "Jog"; + const static PROGMEM prog_uchar preheat[] = "Preheat"; + const static PROGMEM prog_uchar extruder[] = "Extrude"; + const static PROGMEM prog_uchar homeAxis[] = "Home Axis"; + const static PROGMEM prog_uchar advanceABP[] = "Advance ABP"; + const static PROGMEM prog_uchar steppersS[] = "Steppers"; + const static PROGMEM prog_uchar moodlight[] = "Mood Light"; + const static PROGMEM prog_uchar endStops[] = "Test End Stops"; + const static PROGMEM prog_uchar versions[] = "Version"; + const static PROGMEM prog_uchar snake[] = "Snake Game"; switch (index) { case 0: @@ -1531,18 +1532,21 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(homeAxis); break; case 6: - lcd.writeFromPgmspace(steppersS); + lcd.writeFromPgmspace(advanceABP); break; case 7: - lcd.writeFromPgmspace(moodlight); + lcd.writeFromPgmspace(steppersS); break; case 8: - lcd.writeFromPgmspace(endStops); + lcd.writeFromPgmspace(moodlight); break; case 9: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(endStops); break; case 10: + lcd.writeFromPgmspace(versions); + break; + case 11: lcd.writeFromPgmspace(snake); break; } @@ -1576,22 +1580,26 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&homeAxisMode); break; case 6: + // Show advance ABP + interface::pushScreen(&advanceABPMode); + break; + case 7: // Show steppers menu interface::pushScreen(&steppersMenu); break; - case 7: + case 8: // Show Mood Light Mode interface::pushScreen(&moodLightMode); break; - case 8: + case 9: // Show test end stops menu interface::pushScreen(&testEndStopsMode); break; - case 9: + case 10: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 10: + case 11: // Show build from SD screen interface::pushScreen(&snake); break; @@ -2378,4 +2386,54 @@ void PauseAtZPosScreen::notifyButtonPressed(ButtonArray::ButtonName button) { if ( pauseAtZPos < 0.001 ) pauseAtZPos = 0.0; } +void AdvanceABPMode::reset() { + abpForwarding = false; +} + +void AdvanceABPMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar abp1[] = "Advance ABP:"; + const static PROGMEM prog_uchar abp2[] = "hold key..."; + const static PROGMEM prog_uchar abp3[] = " (fwd)"; + + if (forceRedraw) { + lcd.clear(); + lcd.setCursor(0,0); + lcd.writeFromPgmspace(abp1); + + lcd.setCursor(0,1); + lcd.writeFromPgmspace(abp2); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(abp3); + } + + if (( abpForwarding ) && ( ! interface::isButtonPressed(ButtonArray::OK) )) { + OutPacket responsePacket; + + abpForwarding = false; + extruderControl(SLAVE_CMD_TOGGLE_ABP, EXTDR_CMD_SET8, responsePacket, (uint16_t)0); + } +} + +void AdvanceABPMode::notifyButtonPressed(ButtonArray::ButtonName button) { + OutPacket responsePacket; + + switch (button) { + case ButtonArray::OK: + abpForwarding = true; + extruderControl(SLAVE_CMD_TOGGLE_ABP, EXTDR_CMD_SET8, responsePacket, (uint16_t)1); + break; + case ButtonArray::YMINUS: + case ButtonArray::ZMINUS: + case ButtonArray::YPLUS: + case ButtonArray::ZPLUS: + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + case ButtonArray::ZERO: + case ButtonArray::CANCEL: + interface::popScreen(); + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index edb53ca..9bdcd21 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -474,6 +474,20 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class AdvanceABPMode: public Screen { +private: + bool abpForwarding; + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -492,6 +506,7 @@ private: ExtruderMode extruderMenu; HomeAxisMode homeAxisMode; SteppersMenu steppersMenu; + AdvanceABPMode advanceABPMode; TestEndStopsMode testEndStopsMode; VersionMode versionMode; MoodLightMode moodLightMode; From 58ac2106d1c9fa317c6a3e87e5e38b55928eeb4f Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 13 Jan 2012 22:52:12 -0700 Subject: [PATCH 26/61] Added Calibration Menu and Home Offset Menu (measured in steps) --- firmware/src/shared/Menu.cc | 301 +++++++++++++++++++++++++++++++++++- firmware/src/shared/Menu.hh | 51 ++++++ 2 files changed, 346 insertions(+), 6 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index c1fd57d..dd1fcfa 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -33,7 +33,7 @@ int16_t overrideExtrudeSeconds = 0; -Point pausedPosition; +Point pausedPosition, homePosition; void strcat(char *buf, const char* str) @@ -1494,7 +1494,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 12; + itemCount = 14; reset(); } @@ -1508,6 +1508,8 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar advanceABP[] = "Advance ABP"; const static PROGMEM prog_uchar steppersS[] = "Steppers"; const static PROGMEM prog_uchar moodlight[] = "Mood Light"; + const static PROGMEM prog_uchar calibrate[] = "Calibrate"; + const static PROGMEM prog_uchar homeOffsets[] = "Home Offsets"; const static PROGMEM prog_uchar endStops[] = "Test End Stops"; const static PROGMEM prog_uchar versions[] = "Version"; const static PROGMEM prog_uchar snake[] = "Snake Game"; @@ -1541,12 +1543,18 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(moodlight); break; case 9: - lcd.writeFromPgmspace(endStops); + lcd.writeFromPgmspace(calibrate); break; case 10: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(homeOffsets); break; case 11: + lcd.writeFromPgmspace(endStops); + break; + case 12: + lcd.writeFromPgmspace(versions); + break; + case 13: lcd.writeFromPgmspace(snake); break; } @@ -1592,14 +1600,22 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&moodLightMode); break; case 9: + // Show Calibrate Mode + interface::pushScreen(&calibrateMode); + break; + case 10: + // Show Home Offsets Mode + interface::pushScreen(&homeOffsetsMode); + break; + case 11: // Show test end stops menu interface::pushScreen(&testEndStopsMode); break; - case 10: + case 12: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 11: + case 13: // Show build from SD screen interface::pushScreen(&snake); break; @@ -2436,4 +2452,277 @@ void AdvanceABPMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } +void CalibrateMode::reset() { + //Disable stepps on axis 0, 1, 2, 3, 4 + steppers::enableAxis(0, false); + steppers::enableAxis(1, false); + steppers::enableAxis(2, false); + steppers::enableAxis(3, false); + steppers::enableAxis(4, false); + + lastCalibrationState = CS_NONE; + calibrationState = CS_START1; +} + +void CalibrateMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar calib1[] = "Calibrate: Move "; + const static PROGMEM prog_uchar calib2[] = "build platform"; + const static PROGMEM prog_uchar calib3[] = "until nozzle..."; + const static PROGMEM prog_uchar calib4[] = " (cont)"; + const static PROGMEM prog_uchar calib5[] = "lies in center,"; + const static PROGMEM prog_uchar calib6[] = "turn threaded"; + const static PROGMEM prog_uchar calib7[] = "rod until..."; + const static PROGMEM prog_uchar calib8[] = "nozzle just"; + const static PROGMEM prog_uchar calib9[] = "touches."; + const static PROGMEM prog_uchar homeZ[] = "Homing Z..."; + const static PROGMEM prog_uchar homeY[] = "Homing Y..."; + const static PROGMEM prog_uchar homeX[] = "Homing X..."; + const static PROGMEM prog_uchar done[] = "! Calibrated !"; + const static PROGMEM prog_uchar regen[] = "Regenerate gcode"; + const static PROGMEM prog_uchar reset[] = " (reset)"; + + if ((forceRedraw) || (calibrationState != lastCalibrationState)) { + lcd.clear(); + lcd.setCursor(0,0); + switch(calibrationState) { + case CS_START1: + lcd.writeFromPgmspace(calib1); + lcd.setCursor(0,1); + lcd.writeFromPgmspace(calib2); + lcd.setCursor(0,2); + lcd.writeFromPgmspace(calib3); + lcd.setCursor(0,3); + lcd.writeFromPgmspace(calib4); + break; + case CS_START2: + lcd.writeFromPgmspace(calib5); + lcd.setCursor(0,1); + lcd.writeFromPgmspace(calib6); + lcd.setCursor(0,2); + lcd.writeFromPgmspace(calib7); + lcd.setCursor(0,3); + lcd.writeFromPgmspace(calib4); + break; + case CS_PROMPT_MOVE: + lcd.writeFromPgmspace(calib8); + lcd.setCursor(0,1); + lcd.writeFromPgmspace(calib9); + lcd.setCursor(0,3); + lcd.writeFromPgmspace(calib4); + break; + case CS_HOME_Z: + case CS_HOME_Z_WAIT: + lcd.writeFromPgmspace(homeZ); + break; + case CS_HOME_Y: + case CS_HOME_Y_WAIT: + lcd.writeFromPgmspace(homeY); + break; + case CS_HOME_X: + case CS_HOME_X_WAIT: + lcd.writeFromPgmspace(homeX); + break; + case CS_PROMPT_CALIBRATED: + lcd.writeFromPgmspace(done); + lcd.setCursor(0,1); + lcd.writeFromPgmspace(regen); + lcd.setCursor(0,3); + lcd.writeFromPgmspace(reset); + break; + } + } + + lastCalibrationState = calibrationState; + + //Change the state + //Some states are changed when a button is pressed via notifyButton + //Some states are changed when something completes, in which case we do it here + uint8_t axes; + + switch(calibrationState) { + case CS_HOME_Z: + //Declare current position to be x=0, y=0, z=0, a=0, b=0 + steppers::definePosition(Point(0,0,0,0,0)); + steppers::startHoming(true, 0x04, (uint32_t)2000); + calibrationState = CS_HOME_Z_WAIT; + break; + case CS_HOME_Z_WAIT: + if ( ! steppers::isHoming() ) calibrationState = CS_HOME_Y; + break; + case CS_HOME_Y: + steppers::startHoming(false, 0x02, (uint32_t)2000); + calibrationState = CS_HOME_Y_WAIT; + break; + case CS_HOME_Y_WAIT: + if ( ! steppers::isHoming() ) calibrationState = CS_HOME_X; + break; + case CS_HOME_X: + steppers::startHoming(false, 0x01, (uint32_t)2000); + calibrationState = CS_HOME_X_WAIT; + break; + case CS_HOME_X_WAIT: + if ( ! steppers::isHoming() ) { + //Record current X, Y, Z, A, B co-ordinates to the motherboard + for (uint8_t i = 0; i < STEPPER_COUNT; i++) { + uint16_t offset = eeprom::AXIS_HOME_POSITIONS + 4*i; + uint32_t position = steppers::getPosition()[i]; + cli(); + eeprom_write_block(&position, (void*) offset, 4); + sei(); + } + + //Disable stepps on axis 0, 1, 2, 3, 4 + steppers::enableAxis(0, false); + steppers::enableAxis(1, false); + steppers::enableAxis(2, false); + steppers::enableAxis(3, false); + steppers::enableAxis(4, false); + + calibrationState = CS_PROMPT_CALIBRATED; + } + break; + } +} + +void CalibrateMode::notifyButtonPressed(ButtonArray::ButtonName button) { + + if ( calibrationState == CS_PROMPT_CALIBRATED ) { + host::stopBuild(); + return; + } + + switch (button) { + case ButtonArray::OK: + case ButtonArray::YMINUS: + case ButtonArray::ZMINUS: + case ButtonArray::YPLUS: + case ButtonArray::ZPLUS: + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + case ButtonArray::ZERO: + if (( calibrationState == CS_START1 ) || ( calibrationState == CS_START2 ) || + (calibrationState == CS_PROMPT_MOVE )) calibrationState = (enum calibrateState)((uint8_t)calibrationState + 1); + break; + case ButtonArray::CANCEL: + interface::popScreen(); + break; + } +} + +void HomeOffsetsMode::reset() { + homePosition = steppers::getPosition(); + + for (uint8_t i = 0; i < STEPPER_COUNT; i++) { + uint16_t offset = eeprom::AXIS_HOME_POSITIONS + 4*i; + cli(); + eeprom_read_block(&(homePosition[i]), (void*) offset, 4); + sei(); + } + + lastHomeOffsetState = HOS_NONE; + homeOffsetState = HOS_OFFSET_X; +} + +void HomeOffsetsMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1x[] = "X Offset(steps):"; + const static PROGMEM prog_uchar message1y[] = "Y Offset(steps):"; + const static PROGMEM prog_uchar message1z[] = "Z Offset(steps):"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if ( homeOffsetState != lastHomeOffsetState ) forceRedraw = true; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + switch(homeOffsetState) { + case HOS_OFFSET_X: + lcd.writeFromPgmspace(message1x); + break; + case HOS_OFFSET_Y: + lcd.writeFromPgmspace(message1y); + break; + case HOS_OFFSET_Z: + lcd.writeFromPgmspace(message1z); + break; + } + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + float position = 0.0; + + switch(homeOffsetState) { + case HOS_OFFSET_X: + position = (float)homePosition[0]; + break; + case HOS_OFFSET_Y: + position = (float)homePosition[1]; + break; + case HOS_OFFSET_Z: + position = (float)homePosition[2]; + break; + } + + lcd.setCursor(0,1); + lcd.writeFloat((float)position, 0); + + lastHomeOffsetState = homeOffsetState; +} + +void HomeOffsetsMode::notifyButtonPressed(ButtonArray::ButtonName button) { + if (( homeOffsetState == HOS_OFFSET_Z ) && (button == ButtonArray::OK )) { + //Write the new home positions + for (uint8_t i = 0; i < STEPPER_COUNT; i++) { + uint16_t offset = eeprom::AXIS_HOME_POSITIONS + 4*i; + uint32_t position = homePosition[i]; + cli(); + eeprom_write_block(&position, (void*) offset, 4); + sei(); + } + + host::stopBuild(); + return; + } + + float currentPosition; + uint8_t currentIndex = homeOffsetState - HOS_OFFSET_X; + + currentPosition = (float)homePosition[currentIndex]; + + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + case ButtonArray::OK: + if ( homeOffsetState == HOS_OFFSET_X ) homeOffsetState = HOS_OFFSET_Y; + else if ( homeOffsetState == HOS_OFFSET_Y ) homeOffsetState = HOS_OFFSET_Z; + break; + case ButtonArray::ZPLUS: + // increment more + currentPosition += 5.0; + break; + case ButtonArray::ZMINUS: + // decrement more + currentPosition -= 5.0; + break; + case ButtonArray::YPLUS: + // increment less + currentPosition += 1; + break; + case ButtonArray::YMINUS: + // decrement less + currentPosition -= 1.0; + break; + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } + + homePosition[currentIndex] = (int32_t)currentPosition; +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 9bdcd21..d8dd224 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -488,6 +488,55 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class CalibrateMode: public Screen { +private: + enum calibrateState { + CS_NONE, + CS_START1, //Disable steppers + CS_START2, //Disable steppers + CS_PROMPT_MOVE, //Prompt user to move build platform + CS_HOME_Z, + CS_HOME_Z_WAIT, + CS_HOME_Y, + CS_HOME_Y_WAIT, + CS_HOME_X, + CS_HOME_X_WAIT, + CS_PROMPT_CALIBRATED + }; + + enum calibrateState calibrationState, lastCalibrationState; + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + +class HomeOffsetsMode: public Screen { +private: + enum homeOffState { + HOS_NONE, + HOS_OFFSET_X, + HOS_OFFSET_Y, + HOS_OFFSET_Z, + }; + + enum homeOffState homeOffsetState, lastHomeOffsetState; + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -507,6 +556,8 @@ private: HomeAxisMode homeAxisMode; SteppersMenu steppersMenu; AdvanceABPMode advanceABPMode; + CalibrateMode calibrateMode; + HomeOffsetsMode homeOffsetsMode; TestEndStopsMode testEndStopsMode; VersionMode versionMode; MoodLightMode moodLightMode; From 60f102fbf511e3e21de85304314b0ab3463c3336 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Sun, 15 Jan 2012 19:42:33 -0700 Subject: [PATCH 27/61] Workaround for hardware bug when USB is connected and power is turned on, and junk is displayed on LCD Screen. Connect ATX Pin 8 (Power Good) to Arduino Analog Pin 10, then motherboard will reset when a power good is asserted after not being asserted. --- firmware/src/Motherboard/Main.cc | 27 +++++++++++++++++++ .../Motherboard/boards/mb24/Configuration.hh | 4 +++ 2 files changed, 31 insertions(+) diff --git a/firmware/src/Motherboard/Main.cc b/firmware/src/Motherboard/Main.cc index a4d3f4b..d5cf63b 100644 --- a/firmware/src/Motherboard/Main.cc +++ b/firmware/src/Motherboard/Main.cc @@ -30,6 +30,14 @@ #include "EepromMap.hh" #include "Errors.hh" + +#ifdef HAS_MOOD_LIGHT +/// Workaround for hardware issue, where powering on with USB connected +/// cause blank LCD, or corrupted LCD, requiring reset. +volatile bool atxLastPowerGood; +#endif + + void reset(bool hard_reset) { ATOMIC_BLOCK(ATOMIC_FORCEON) { Motherboard& board = Motherboard::getBoard(); @@ -47,6 +55,12 @@ void reset(bool hard_reset) { } #endif +#ifdef HAS_ATX_POWER_GOOD + /// Workaround for hardware issue, where powering on with USB connected + /// cause blank LCD, or corrupted LCD, requiring reset. + ATX_POWER_GOOD.setDirection(false); + atxLastPowerGood = ATX_POWER_GOOD.getValue(); +#endif sei(); // If we've just come from a hard reset, wait for 2.5 seconds before @@ -80,6 +94,19 @@ int main() { command::runCommandSlice(); // Motherboard slice board.runMotherboardSlice(); + +#ifdef HAS_ATX_POWER_GOOD + /// Workaround for hardware issue, where powering on with USB connected + /// cause blank LCD, or corrupted LCD, requiring reset. + /// If we're running here and the last time we looped, power was not good, + /// and now power is good, someone just hit the power button, so we force a + /// reset + bool powerGood = ATX_POWER_GOOD.getValue(); + if (( ! atxLastPowerGood ) && ( powerGood )) { + reset(true); + } + atxLastPowerGood = powerGood; +#endif } return 0; } diff --git a/firmware/src/Motherboard/boards/mb24/Configuration.hh b/firmware/src/Motherboard/boards/mb24/Configuration.hh index 866a614..2588aad 100644 --- a/firmware/src/Motherboard/boards/mb24/Configuration.hh +++ b/firmware/src/Motherboard/boards/mb24/Configuration.hh @@ -189,4 +189,8 @@ #define SOFTWARE_I2C_SDA_PIN Pin(PortK,0) //Pin d on the BlinkM #define SOFTWARE_I2C_SCL_PIN Pin(PortK,1) //Pin c on the BlinkM +//ATX Power Good +#define HAS_ATX_POWER_GOOD 1 +#define ATX_POWER_GOOD Pin(PortK,2) //Pin ATX 8 connected to Analog 10 + #endif // BOARDS_RRMBV12_CONFIGURATION_HH_ From 5fa302df52b1fa9fc2a8081b1c11d85dde5ec5a0 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 18 Jan 2012 12:53:58 -0700 Subject: [PATCH 28/61] Fixed ifdef on patch commit 60f102f --- firmware/src/Motherboard/Main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/src/Motherboard/Main.cc b/firmware/src/Motherboard/Main.cc index d5cf63b..3b0d72f 100644 --- a/firmware/src/Motherboard/Main.cc +++ b/firmware/src/Motherboard/Main.cc @@ -31,7 +31,7 @@ #include "Errors.hh" -#ifdef HAS_MOOD_LIGHT +#ifdef HAS_ATX_POWER_GOOD /// Workaround for hardware issue, where powering on with USB connected /// cause blank LCD, or corrupted LCD, requiring reset. volatile bool atxLastPowerGood; From 0431835a91874920a7ffbe445b0afcfe6c4a6662 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 18 Jan 2012 16:48:58 -0700 Subject: [PATCH 29/61] Implements Buzzer functionality on V2.4 Motherboard. Buzzer required with Internal Drive (max 20mA). Buzzes for Build Complete, Pause@ZPos and Safety Cutoff triggered. Also, M codes 213/214 can be used to control and issue buzzes via gcode. --- firmware/src/Motherboard/Command.cc | 20 ++++ firmware/src/Motherboard/EepromMap.cc | 1 + firmware/src/Motherboard/EepromMap.hh | 3 + firmware/src/Motherboard/Main.cc | 1 + .../Motherboard/boards/mb24/Motherboard.cc | 81 +++++++++++++ .../Motherboard/boards/mb24/Motherboard.hh | 25 ++++ firmware/src/shared/Commands.hh | 2 + firmware/src/shared/Menu.cc | 111 +++++++++++++++--- firmware/src/shared/Menu.hh | 16 +++ 9 files changed, 245 insertions(+), 15 deletions(-) diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index c352a2a..a36cfaa 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -449,6 +449,26 @@ void runCommandSlice() { int32_t writeToEeprom = pop32(); #ifdef HAS_MOOD_LIGHT Motherboard::getBoard().MoodLightPlayScript((uint8_t)scriptId, (uint8_t)writeToEeprom); +#endif + } + } else if (command == HOST_CMD_BUZZER_REPEATS ) { + // check for completion + if (command_buffer.getLength() >= 2) { + command_buffer.pop(); // remove the command code + cli(); + eeprom_write_byte((uint8_t*)eeprom::BUZZER_REPEATS, pop8()); + sei(); + } + } else if (command == HOST_CMD_BUZZER_BUZZ ) { + // check for completion + if (command_buffer.getLength() >= 7) { + command_buffer.pop(); // remove the command code + uint8_t buzzes = pop8(); + uint8_t duration = pop8(); + uint8_t repeats = pop8(); +#ifdef HAS_BUZZER + if ( buzzes == 0 ) Motherboard::getBoard().stopBuzzer(); + else Motherboard::getBoard().buzz(buzzes, duration, repeats); #endif } } else { diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index bf74275..83e8f90 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -40,6 +40,7 @@ void setDefaults() { eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_GREEN,255); eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_BLUE,255); eeprom_write_byte((uint8_t*)eeprom::JOG_MODE_SETTINGS,0); + eeprom_write_byte((uint8_t*)eeprom::BUZZER_REPEATS,3); } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index edb42a2..ad25542 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -72,6 +72,9 @@ const static uint16_t MOOD_LIGHT_CUSTOM_BLUE = 0x0088; //Bit 2-4 are the jog mode distance 0 = short, 1 = long, 2 = cont const static uint16_t JOG_MODE_SETTINGS = 0x0089; +//0 = No system buzzing, >=1 = number of repeats to buzz for +const static uint16_t BUZZER_REPEATS = 0x008A; + /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/Motherboard/Main.cc b/firmware/src/Motherboard/Main.cc index 3b0d72f..1e13147 100644 --- a/firmware/src/Motherboard/Main.cc +++ b/firmware/src/Motherboard/Main.cc @@ -121,6 +121,7 @@ ISR(ESTOP_vect, ISR_NOBLOCK) { command::reset(); UART::getHostUART().enable(false); Motherboard::getBoard().indicateError(ERR_ESTOP); + Motherboard::getBoard().buzz(7, 10, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); } #endif diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index 49140fc..9166f89 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -133,6 +133,12 @@ void Motherboard::reset() { TCCR2A = 0x00; TCCR2B = 0x07; // prescaler at 1/1024 TIMSK2 = 0x01; // OVF flag on + + buzzerRepeats = 0; + buzzerDuration = 0.0; + buzzerState = BUZZ_STATE_NONE; + BUZZER_PIN.setDirection(false); + // Configure the debug pin. DEBUG_PIN.setDirection(true); @@ -216,6 +222,8 @@ void Motherboard::runMotherboardSlice() { interface_update_timeout.start(interfaceBoard.getUpdateRate()); } } + + serviceBuzzer(); } MoodLightController Motherboard::getMoodLightController() { @@ -277,6 +285,79 @@ void Motherboard::MoodLightPlayScript(uint8_t scriptId, uint8_t writeToEeprom) { moodLightController.playScript(scriptId); } +//Duration is the length of each buzz in 1/10secs +//Issue "repeats = 0" to kill a current buzzing + +void Motherboard::buzz(uint8_t buzzes, uint8_t duration, uint8_t repeats) { + if ( repeats == 0 ) { + buzzerState = BUZZ_STATE_NONE; + return; + } + + buzzerBuzzes = buzzes; + buzzerBuzzesReset = buzzes; + buzzerDuration = (float)duration / 10.0; + buzzerRepeats = repeats; + + BUZZER_PIN.setDirection(true); + buzzerState = BUZZ_STATE_MOVE_TO_ON; +} + +void Motherboard::stopBuzzer() { + buzzerState = BUZZ_STATE_NONE; + + BUZZER_PIN.setValue(false); + BUZZER_PIN.setDirection(false); +} + +void Motherboard::serviceBuzzer() { + if ( buzzerState == BUZZ_STATE_NONE ) return; + + float currentSeconds = getCurrentSeconds(); + + switch (buzzerState) + { + case BUZZ_STATE_BUZZ_ON: + if ( currentSeconds >= buzzerSecondsTarget ) + buzzerState = BUZZ_STATE_MOVE_TO_OFF; + break; + case BUZZ_STATE_MOVE_TO_OFF: + buzzerBuzzes --; + BUZZER_PIN.setValue(false); + buzzerSecondsTarget = currentSeconds + buzzerDuration; + buzzerState = BUZZ_STATE_BUZZ_OFF; + break; + case BUZZ_STATE_BUZZ_OFF: + if ( currentSeconds >= buzzerSecondsTarget ) { + if ( buzzerBuzzes == 0 ) { + buzzerRepeats --; + if ( buzzerRepeats == 0 ) stopBuzzer(); + else buzzerState = BUZZ_STATE_MOVE_TO_DELAY; + } else buzzerState = BUZZ_STATE_MOVE_TO_ON; + } + break; + case BUZZ_STATE_MOVE_TO_ON: + BUZZER_PIN.setValue(true); + buzzerSecondsTarget = currentSeconds + buzzerDuration; + buzzerState = BUZZ_STATE_BUZZ_ON; + break; + case BUZZ_STATE_MOVE_TO_DELAY: + BUZZER_PIN.setValue(false); + buzzerSecondsTarget = currentSeconds + buzzerDuration * 3; + buzzerState = BUZZ_STATE_BUZZ_DELAY; + break; + case BUZZ_STATE_BUZZ_DELAY: + if ( currentSeconds >= buzzerSecondsTarget ) { + buzzerBuzzes = buzzerBuzzesReset; + buzzerSecondsTarget = currentSeconds + buzzerDuration; + BUZZER_PIN.setValue(true); + buzzerState = BUZZ_STATE_BUZZ_ON; + } + break; + } +} + + /// Timer2 overflow cycles that the LED remains on while blinking #define OVFS_ON 18 diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.hh b/firmware/src/Motherboard/boards/mb24/Motherboard.hh index 88ecc7b..e68be6d 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.hh +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.hh @@ -72,6 +72,28 @@ private: SplashScreen splashScreen; ///< Displayed at startup MonitorMode monitorMode; ///< Displayed during build + + bool buzzOn; + uint8_t buzzerRepeats; + uint8_t buzzerBuzzes; + uint8_t buzzerBuzzesReset; + float buzzerDuration; + float buzzerSecondsTarget; + + enum BuzzerState { + BUZZ_STATE_NONE = 0, + BUZZ_STATE_MOVE_TO_ON, + BUZZ_STATE_BUZZ_ON, + BUZZ_STATE_MOVE_TO_OFF, + BUZZ_STATE_BUZZ_OFF, + BUZZ_STATE_MOVE_TO_DELAY, + BUZZ_STATE_BUZZ_DELAY + }; + + enum BuzzerState buzzerState; + + void serviceBuzzer(); + public: /// Reset the motherboard to its initial state. /// This only resets the board, and does not send a reset @@ -107,6 +129,9 @@ public: void MoodLightSetRGBColor(uint8_t r, uint8_t g, uint8_t b, uint8_t fadeSpeed, uint8_t writeToEeprom); void MoodLightSetHSBColor(uint8_t r, uint8_t g, uint8_t b, uint8_t fadeSpeed); void MoodLightPlayScript(uint8_t scriptId, uint8_t writeToEeprom); + + void buzz(uint8_t buzzes, uint8_t duration, uint8_t repeats); + void stopBuzzer(); }; #endif // BOARDS_MB24_MOTHERBOARD_HH_ diff --git a/firmware/src/shared/Commands.hh b/firmware/src/shared/Commands.hh index 03b30e9..6b93bdd 100644 --- a/firmware/src/shared/Commands.hh +++ b/firmware/src/shared/Commands.hh @@ -107,6 +107,8 @@ #define HOST_CMD_MOOD_LIGHT_SET_RGB 210 #define HOST_CMD_MOOD_LIGHT_SET_HSB 211 #define HOST_CMD_MOOD_LIGHT_PLAY_SCRIPT 212 +#define HOST_CMD_BUZZER_REPEATS 213 +#define HOST_CMD_BUZZER_BUZZ 214 #define HOST_CMD_DEBUG_ECHO 0x70 diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index dd1fcfa..6ca9dc3 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1045,6 +1045,7 @@ void MonitorMode::reset() { lastElapsedSeconds = 0.0; pausePushLockout = false; pauseMode.autoPause = false; + buildCompleteBuzzPlayed = false; } @@ -1151,6 +1152,12 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( (hostState != host::HOST_STATE_BUILDING ) && ( hostState != host::HOST_STATE_BUILDING_FROM_SD )) break; + //Signal buzzer if we're complete + if (( ! buildCompleteBuzzPlayed ) && ( sdcard::getPercentPlayed() >= 100.0 )) { + buildCompleteBuzzPlayed = true; + Motherboard::getBoard().buzz(2, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); + } + float secs; //Holding the zero button stops rotation @@ -1494,7 +1501,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 14; + itemCount = 15; reset(); } @@ -1508,6 +1515,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar advanceABP[] = "Advance ABP"; const static PROGMEM prog_uchar steppersS[] = "Steppers"; const static PROGMEM prog_uchar moodlight[] = "Mood Light"; + const static PROGMEM prog_uchar buzzer[] = "Buzzer"; const static PROGMEM prog_uchar calibrate[] = "Calibrate"; const static PROGMEM prog_uchar homeOffsets[] = "Home Offsets"; const static PROGMEM prog_uchar endStops[] = "Test End Stops"; @@ -1543,18 +1551,21 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(moodlight); break; case 9: - lcd.writeFromPgmspace(calibrate); + lcd.writeFromPgmspace(buzzer); break; case 10: - lcd.writeFromPgmspace(homeOffsets); + lcd.writeFromPgmspace(calibrate); break; case 11: - lcd.writeFromPgmspace(endStops); + lcd.writeFromPgmspace(homeOffsets); break; case 12: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(endStops); break; case 13: + lcd.writeFromPgmspace(versions); + break; + case 14: lcd.writeFromPgmspace(snake); break; } @@ -1599,23 +1610,27 @@ void MainMenu::handleSelect(uint8_t index) { // Show Mood Light Mode interface::pushScreen(&moodLightMode); break; - case 9: + case 9: + // Show Buzzer Mode + interface::pushScreen(&buzzerSetRepeats); + break; + case 10: // Show Calibrate Mode interface::pushScreen(&calibrateMode); break; - case 10: + case 11: // Show Home Offsets Mode interface::pushScreen(&homeOffsetsMode); break; - case 11: + case 12: // Show test end stops menu interface::pushScreen(&testEndStopsMode); break; - case 12: + case 13: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 13: + case 14: // Show build from SD screen interface::pushScreen(&snake); break; @@ -2289,17 +2304,22 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { } break; - case 3: //We're now paused + case 3: //Buzz if we're a Pause@ZPos + if ( autoPause ) Motherboard::getBoard().buzz(4, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); + pauseState ++; + break; + + case 4: //We're now paused break; - case 4: //Leaving paused, wait for any steppers to finish + case 5: //Leaving paused, wait for any steppers to finish if ( autoPause ) command::pauseAtZPos(0.0); lcd.clear(); lcd.writeFromPgmspace(leavingPaused); if ( ! steppers::isRunning()) pauseState ++; break; - case 5: //Return to original position + case 6: //Return to original position lcd.writeFromPgmspace(leavingPaused); //The extruders may have moved, so it doesn't make sense @@ -2312,7 +2332,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { pauseState ++; break; - case 6: //Wait for return to original position + case 7: //Wait for return to original position lcd.writeFromPgmspace(leavingPaused); if ( ! steppers::isRunning()) { pauseState = 0; @@ -2332,7 +2352,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { void PauseMode::notifyButtonPressed(ButtonArray::ButtonName button) { if ( button == ButtonArray::CANCEL ) { - if ( pauseState == 3 ) pauseState ++; + if ( pauseState == 4 ) pauseState ++; } else jog(button); } @@ -2725,4 +2745,65 @@ void HomeOffsetsMode::notifyButtonPressed(ButtonArray::ButtonName button) { homePosition[currentIndex] = (int32_t)currentPosition; } +void BuzzerSetRepeatsMode::reset() { + repeats = eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3); +} + +void BuzzerSetRepeatsMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Repeat Buzzer:"; + const static PROGMEM prog_uchar message2[] = "(0=Buzzer Off)"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar times[] = " times "; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,1); + lcd.writeFromPgmspace(message2); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(0,2); + lcd.writeInt(repeats, 3); + lcd.writeFromPgmspace(times); +} + +void BuzzerSetRepeatsMode::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + case ButtonArray::OK: + eeprom_write_byte((uint8_t *)eeprom::BUZZER_REPEATS, repeats); + interface::popScreen(); + break; + case ButtonArray::ZPLUS: + // increment more + if (repeats <= 249) repeats += 5; + break; + case ButtonArray::ZMINUS: + // decrement more + if (repeats >= 5) repeats -= 5; + break; + case ButtonArray::YPLUS: + // increment less + if (repeats <= 253) repeats += 1; + break; + case ButtonArray::YMINUS: + // decrement less + if (repeats >= 1) repeats -= 1; + break; + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index d8dd224..f966bee 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -264,6 +264,7 @@ private: bool buildComplete; //For solving floating point rounding issues PauseMode pauseMode; bool pausePushLockout; + bool buildCompleteBuzzPlayed; public: micros_t getUpdateRate() {return 500L * 1000L;} @@ -537,6 +538,20 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class BuzzerSetRepeatsMode: public Screen { +private: + uint8_t repeats; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -556,6 +571,7 @@ private: HomeAxisMode homeAxisMode; SteppersMenu steppersMenu; AdvanceABPMode advanceABPMode; + BuzzerSetRepeatsMode buzzerSetRepeats; CalibrateMode calibrateMode; HomeOffsetsMode homeOffsetsMode; TestEndStopsMode testEndStopsMode; From dc7fd689b3f2a6903e5f547d5ba4dbb004831253 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 18 Jan 2012 17:44:56 -0700 Subject: [PATCH 30/61] Added Extruder Fan menu item to switch the Extruder Fan On / Off. --- firmware/src/shared/Menu.cc | 75 ++++++++++++++++++++++++++++++++----- firmware/src/shared/Menu.hh | 12 ++++++ 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 6ca9dc3..d36fc02 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1501,7 +1501,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { - itemCount = 15; + itemCount = 16; reset(); } @@ -1516,6 +1516,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar steppersS[] = "Steppers"; const static PROGMEM prog_uchar moodlight[] = "Mood Light"; const static PROGMEM prog_uchar buzzer[] = "Buzzer"; + const static PROGMEM prog_uchar extruderFan[] = "Extruder Fan"; const static PROGMEM prog_uchar calibrate[] = "Calibrate"; const static PROGMEM prog_uchar homeOffsets[] = "Home Offsets"; const static PROGMEM prog_uchar endStops[] = "Test End Stops"; @@ -1554,18 +1555,21 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(buzzer); break; case 10: - lcd.writeFromPgmspace(calibrate); + lcd.writeFromPgmspace(extruderFan); break; case 11: - lcd.writeFromPgmspace(homeOffsets); + lcd.writeFromPgmspace(calibrate); break; case 12: - lcd.writeFromPgmspace(endStops); + lcd.writeFromPgmspace(homeOffsets); break; case 13: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(endStops); break; case 14: + lcd.writeFromPgmspace(versions); + break; + case 15: lcd.writeFromPgmspace(snake); break; } @@ -1614,23 +1618,27 @@ void MainMenu::handleSelect(uint8_t index) { // Show Buzzer Mode interface::pushScreen(&buzzerSetRepeats); break; - case 10: + case 10: + // Show Extruder Fan Mode + interface::pushScreen(&extruderFanMenu); + break; + case 11: // Show Calibrate Mode interface::pushScreen(&calibrateMode); break; - case 11: + case 12: // Show Home Offsets Mode interface::pushScreen(&homeOffsetsMode); break; - case 12: + case 13: // Show test end stops menu interface::pushScreen(&testEndStopsMode); break; - case 13: + case 14: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 14: + case 15: // Show build from SD screen interface::pushScreen(&snake); break; @@ -2806,4 +2814,51 @@ void BuzzerSetRepeatsMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } +ExtruderFanMenu::ExtruderFanMenu() { + itemCount = 4; + reset(); +} + +void ExtruderFanMenu::resetState() { + itemIndex = 2; + firstItemIndex = 2; +} + +void ExtruderFanMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar title[] = "Extruder Fan:"; + const static PROGMEM prog_uchar off[] = "Off"; + const static PROGMEM prog_uchar on[] = "On"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(title); + break; + case 1: + break; + case 2: + lcd.writeFromPgmspace(off); + break; + case 3: + lcd.writeFromPgmspace(on); + break; + } +} + +void ExtruderFanMenu::handleSelect(uint8_t index) { + OutPacket responsePacket; + + switch (index) { + case 2: + //Disable Cooling Fan + extruderControl(SLAVE_CMD_TOGGLE_FAN, EXTDR_CMD_SET, responsePacket, 0); + interface::popScreen(); + break; + case 3: + //Enable Cooling Fan + extruderControl(SLAVE_CMD_TOGGLE_FAN, EXTDR_CMD_SET, responsePacket, 1); + interface::popScreen(); + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index f966bee..a56e5b9 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -552,6 +552,17 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class ExtruderFanMenu: public Menu { +public: + ExtruderFanMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -572,6 +583,7 @@ private: SteppersMenu steppersMenu; AdvanceABPMode advanceABPMode; BuzzerSetRepeatsMode buzzerSetRepeats; + ExtruderFanMenu extruderFanMenu; CalibrateMode calibrateMode; HomeOffsetsMode homeOffsetsMode; TestEndStopsMode testEndStopsMode; From 5ef28af2d2e191f716e515a8788f94a3f0a0d3e0 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 18 Jan 2012 19:12:35 -0700 Subject: [PATCH 31/61] Added filament retraction to Pause / Pause@ZPos to remove most of the plastic blob created during a pause. --- firmware/src/shared/Menu.cc | 38 ++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index d36fc02..f60b90c 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -2261,6 +2261,7 @@ void PauseMode::jog(ButtonArray::ButtonName direction) { void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar waitForCurrentCommand[] = "Entering pause.."; + const static PROGMEM prog_uchar retractFilament[] = "Retract Filament"; const static PROGMEM prog_uchar movingZ[] = "Moving Z up 2mm "; const static PROGMEM prog_uchar leavingPaused[] = "Leaving pause.. "; const static PROGMEM prog_uchar paused1[] = "Paused: "; @@ -2282,19 +2283,38 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( ! steppers::isRunning()) pauseState ++; break; - case 1: //Last command finished, record current position and move + case 1: //Last command finished, record current position and + //retract filament + lcd.writeFromPgmspace(retractFilament); + + pausedPosition = steppers::getPosition(); + newPosition = pausedPosition; + newPosition[3] += 50; //Retract the filament so we don't get blobs + steppers::setTarget(newPosition, interval / 4); + + pauseState ++; + break; + + case 2: //Wait for the retract to complete + lcd.writeFromPgmspace(retractFilament); + if ( ! steppers::isRunning()) { + pauseState ++; + } + break; + + case 3: //Last command finished, record current position and move //Z away from build lcd.writeFromPgmspace(movingZ); pausedPosition = steppers::getPosition(); newPosition = pausedPosition; newPosition[2] += 2 * 200; //200 because of the number of steps per mm - steppers::setTarget(newPosition, interval); + steppers::setTarget(newPosition, interval / 4); pauseState ++; break; - case 2: //Wait for the Z move up to complete + case 4: //Wait for the Z move up to complete lcd.writeFromPgmspace(movingZ); if ( ! steppers::isRunning()) { pauseState ++; @@ -2312,22 +2332,22 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { } break; - case 3: //Buzz if we're a Pause@ZPos + case 5: //Buzz if we're a Pause@ZPos if ( autoPause ) Motherboard::getBoard().buzz(4, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); pauseState ++; break; - case 4: //We're now paused + case 6: //We're now paused break; - case 5: //Leaving paused, wait for any steppers to finish + case 7: //Leaving paused, wait for any steppers to finish if ( autoPause ) command::pauseAtZPos(0.0); lcd.clear(); lcd.writeFromPgmspace(leavingPaused); if ( ! steppers::isRunning()) pauseState ++; break; - case 6: //Return to original position + case 8: //Return to original position lcd.writeFromPgmspace(leavingPaused); //The extruders may have moved, so it doesn't make sense @@ -2340,7 +2360,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { pauseState ++; break; - case 7: //Wait for return to original position + case 9: //Wait for return to original position lcd.writeFromPgmspace(leavingPaused); if ( ! steppers::isRunning()) { pauseState = 0; @@ -2360,7 +2380,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { void PauseMode::notifyButtonPressed(ButtonArray::ButtonName button) { if ( button == ButtonArray::CANCEL ) { - if ( pauseState == 4 ) pauseState ++; + if ( pauseState == 6 ) pauseState ++; } else jog(button); } From b99c44448b1156bdb00c53c4e399fd1318499244 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 18 Jan 2012 21:46:23 -0700 Subject: [PATCH 32/61] Fixed packet timeout issue on build from computer --- firmware/src/Motherboard/Main.cc | 3 ++- firmware/src/Motherboard/boards/mb24/Motherboard.cc | 4 ++-- firmware/src/Motherboard/boards/mb24/Motherboard.hh | 2 +- firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc | 2 +- firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/firmware/src/Motherboard/Main.cc b/firmware/src/Motherboard/Main.cc index 1e13147..9e8fc7b 100644 --- a/firmware/src/Motherboard/Main.cc +++ b/firmware/src/Motherboard/Main.cc @@ -45,7 +45,8 @@ void reset(bool hard_reset) { steppers::abort(); command::reset(); eeprom::init(); - board.reset(); + board.reset(hard_reset); + #ifdef HAS_ESTOP const uint8_t estop_conf = eeprom::getEeprom8(eeprom::ESTOP_CONFIGURATION, 0); if (estop_conf == eeprom::ESTOP_CONF_ACTIVE_HIGH) { diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index 9166f89..33138dd 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -97,10 +97,10 @@ Motherboard::Motherboard() : /// Reset the motherboard to its initial state. /// This only resets the board, and does not send a reset /// to any attached toolheads. -void Motherboard::reset() { +void Motherboard::reset(bool hard_reset) { indicateError(0); // turn off blinker - moodLightController.start(); + if ( hard_reset ) moodLightController.start(); // Init steppers uint8_t axis_invert = eeprom::getEeprom8(eeprom::AXIS_INVERSION, 0); diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.hh b/firmware/src/Motherboard/boards/mb24/Motherboard.hh index e68be6d..459422b 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.hh +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.hh @@ -98,7 +98,7 @@ public: /// Reset the motherboard to its initial state. /// This only resets the board, and does not send a reset /// to any attached toolheads. - void reset(); + void reset(bool hard_reset); void runMotherboardSlice(); diff --git a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc index 0a95477..a5fdc45 100644 --- a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc +++ b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc @@ -79,7 +79,7 @@ Motherboard::Motherboard(const Pin& psu_pin) : /// Reset the motherboard to its initial state. /// This only resets the board, and does not send a reset /// to any attached toolheads. -void Motherboard::reset() { +void Motherboard::reset(bool hard_reset) { indicateError(0); // turn off blinker // Init and turn on power supply diff --git a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh index 01cfe1b..0c5f088 100644 --- a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh +++ b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh @@ -47,7 +47,7 @@ public: /// Reset the motherboard to its initial state. /// This only resets the board, and does not send a reset /// to any attached toolheads. - void reset(); + void reset(bool hard_reset); void runMotherboardSlice(); From cd608dd101241cca8dd0964657758257a5c91bbd Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 20 Jan 2012 19:10:42 -0700 Subject: [PATCH 33/61] Added Axis Steps:mm menu to support steps:mm mapping for machines other than ToM. Firmware functions are now calculated based on these settings. Home offsets, Jog now reports in mm. Supports Axis X,Y,Z and Extruder. --- firmware/src/Motherboard/Command.cc | 10 +- firmware/src/Motherboard/Command.hh | 8 +- firmware/src/Motherboard/EepromMap.cc | 5 + firmware/src/Motherboard/EepromMap.hh | 7 + firmware/src/shared/Eeprom.cc | 17 + firmware/src/shared/Eeprom.hh | 12 + firmware/src/shared/LiquidCrystal.cc | 28 ++ firmware/src/shared/LiquidCrystal.hh | 2 + firmware/src/shared/Menu.cc | 440 +++++++++++++++++++++----- firmware/src/shared/Menu.hh | 33 +- 10 files changed, 466 insertions(+), 96 deletions(-) diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index a36cfaa..3c49efa 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -41,7 +41,7 @@ bool paused = false; uint16_t statusDivisor = 0; volatile uint32_t recentCommandClock = 0; volatile uint32_t recentCommandTime = 0; -volatile float pauseZPos = 0.0; +volatile int32_t pauseZPos = 0; uint16_t getRemainingCapacity() { uint16_t sz; @@ -59,11 +59,11 @@ bool isPaused() { return paused; } -void pauseAtZPos(float zpos) { +void pauseAtZPos(int32_t zpos) { pauseZPos = zpos; } -float getPauseAtZPos() { +int32_t getPauseAtZPos() { return pauseZPos; } @@ -197,8 +197,8 @@ void runCommandSlice() { //If PauseAtZPos is enabled, pause when we reach zpos //0.005 because of float point rounding errors, we want //to make sure we stop at the correct place - if (( pauseZPos != 0.0) && ( ! isPaused() ) && - (((float)steppers::getPosition()[2] / 200.0) >= (pauseZPos - 0.005) )) + if (( pauseZPos != 0) && ( ! isPaused() ) && + ( steppers::getPosition()[2]) >= pauseZPos ) pause(true); if (mode == HOMING) { diff --git a/firmware/src/Motherboard/Command.hh b/firmware/src/Motherboard/Command.hh index e8f9f2f..799f4e6 100644 --- a/firmware/src/Motherboard/Command.hh +++ b/firmware/src/Motherboard/Command.hh @@ -41,13 +41,13 @@ void pause(bool pause); /// \return True if it is disabled, false if it is enabled. bool isPaused(); -/// \Pause at >= a Z Position provded in mm +/// \Pause at >= a Z Position provded in steps /// 0 cancels pauseAtZPos -void pauseAtZPos(float zpos); +void pauseAtZPos(int32_t zpos); /// Get the current pauseAtZPos position -/// \return the z position set for pausing, otherwise 0 -float getPauseAtZPos(); +/// \return the z position set for pausing (in steps), otherwise 0 +int32_t getPauseAtZPos(); /// Check the remaining capacity of the command buffer /// \return Amount of space left in the buffer, in bytes diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index 83e8f90..568feb0 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -41,6 +41,11 @@ void setDefaults() { eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_BLUE,255); eeprom_write_byte((uint8_t*)eeprom::JOG_MODE_SETTINGS,0); eeprom_write_byte((uint8_t*)eeprom::BUZZER_REPEATS,3); + putEepromInt64(eeprom::STEPS_PER_MM_X,STEPS_PER_MM_X_DEFAULT); + putEepromInt64(eeprom::STEPS_PER_MM_Y,STEPS_PER_MM_Y_DEFAULT); + putEepromInt64(eeprom::STEPS_PER_MM_Z,STEPS_PER_MM_Z_DEFAULT); + putEepromInt64(eeprom::STEPS_PER_MM_A,STEPS_PER_MM_A_DEFAULT); + putEepromInt64(eeprom::STEPS_PER_MM_B,STEPS_PER_MM_B_DEFAULT); } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index ad25542..94c60aa 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -75,6 +75,13 @@ const static uint16_t JOG_MODE_SETTINGS = 0x0089; //0 = No system buzzing, >=1 = number of repeats to buzz for const static uint16_t BUZZER_REPEATS = 0x008A; +//Steps per mm, each one is 8 bytes long and are stored as int64_t +const static uint16_t STEPS_PER_MM_X = 0x008B; +const static uint16_t STEPS_PER_MM_Y = 0x0093; +const static uint16_t STEPS_PER_MM_Z = 0x009B; +const static uint16_t STEPS_PER_MM_A = 0x00A3; +const static uint16_t STEPS_PER_MM_B = 0x00AB; + /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/shared/Eeprom.cc b/firmware/src/shared/Eeprom.cc index 693beb1..ba6b7ca 100644 --- a/firmware/src/shared/Eeprom.cc +++ b/firmware/src/shared/Eeprom.cc @@ -40,4 +40,21 @@ float getEepromFixed16(const uint16_t location, const float default_value) { return ((float)data[0]) + ((float)data[1])/256.0; } +int64_t getEepromInt64(const uint16_t location, const int64_t default_value) { + int64_t *ret; + uint8_t data[8]; + eeprom_read_block(data,(const uint8_t*)location,8); + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff && data[3] == 0xff && + data[4] == 0xff && data[5] == 0xff && data[6] == 0xff && data[7] == 0xff) + return default_value; + ret = (int64_t *)&data[0]; + return *ret; +} + +void putEepromInt64(const uint16_t location, const int64_t value) { + void *data; + data = (void *)&value; + eeprom_write_block(data,(void*)location,8); +} + } // namespace eeprom diff --git a/firmware/src/shared/Eeprom.hh b/firmware/src/shared/Eeprom.hh index ab3da86..668ef2e 100644 --- a/firmware/src/shared/Eeprom.hh +++ b/firmware/src/shared/Eeprom.hh @@ -10,7 +10,19 @@ void init(); uint8_t getEeprom8(const uint16_t location, const uint8_t default_value); uint16_t getEeprom16(const uint16_t location, const uint16_t default_value); float getEepromFixed16(const uint16_t location, const float default_value); +int64_t getEepromInt64(const uint16_t location, const int64_t default_value); +void putEepromInt64(const uint16_t location, const int64_t value); } +#define STEPS_PER_MM_PADDING 5 +#define STEPS_PER_MM_PRECISION 10 +#define STEPS_PER_MM_LOWER_LIMIT 10000000 +#define STEPS_PER_MM_UPPER_LIMIT 200000000000000 +#define STEPS_PER_MM_X_DEFAULT 470698520000 //47.069852 +#define STEPS_PER_MM_Y_DEFAULT 470698520000 //47.069852 +#define STEPS_PER_MM_Z_DEFAULT 2000000000000 //200.0 +#define STEPS_PER_MM_A_DEFAULT 502354788069 //50.2354788069 +#define STEPS_PER_MM_B_DEFAULT 502354788069 //50.2354788069 + #endif // EEPROM_HH diff --git a/firmware/src/shared/LiquidCrystal.cc b/firmware/src/shared/LiquidCrystal.cc index 0513e70..c947a7c 100755 --- a/firmware/src/shared/LiquidCrystal.cc +++ b/firmware/src/shared/LiquidCrystal.cc @@ -342,6 +342,34 @@ void LiquidCrystal::writeFloat(float value, uint8_t decimalPlaces) { } } +//Writes a fixed point number stored in padding.precision format +//where numbers are padded with leading zeros to "padding", and +//Displays "overflow" if the number doesn't fit within padding +//Example: writeFixedPoint(2000 00000, 5, 5) displays 02000.00000 + +void LiquidCrystal::writeFixedPoint(int64_t value, uint8_t padding, uint8_t precision) { + const static PROGMEM prog_uchar overflow[] = "overflow"; + + int64_t divisor = 1; + for (uint8_t i = 0; i < (padding + precision); i ++ ) + divisor *= 10; + + if (( value / divisor ) > 0) { + writeFromPgmspace(overflow); + return; + } + + uint8_t i = 0; + do { + divisor /= 10; + if ( i == padding ) write('.'); + write(((uint8_t)(value / divisor)) + '0'); + value %= divisor; + i ++; + } + while ( divisor > 1 ); +} + void LiquidCrystal::writeString(char message[]) { char* letter = message; while (*letter != 0) { diff --git a/firmware/src/shared/LiquidCrystal.hh b/firmware/src/shared/LiquidCrystal.hh index 081f73d..e729727 100755 --- a/firmware/src/shared/LiquidCrystal.hh +++ b/firmware/src/shared/LiquidCrystal.hh @@ -89,6 +89,8 @@ public: void writeFloat(float value, uint8_t decimalPlaces); + void writeFixedPoint(int64_t value, uint8_t padding, uint8_t precision); + void writeString(char message[]); void writeFromPgmspace(const prog_uchar message[]); diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index f60b90c..107f5f8 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -35,6 +35,48 @@ int16_t overrideExtrudeSeconds = 0; Point pausedPosition, homePosition; +//Stored using STEPS_PER_MM_PRECISION +int64_t axisStepsPerMM[5]; + + +enum Axis { + AXIS_X = 0, + AXIS_Y, + AXIS_Z, + AXIS_A, + AXIS_B +}; + + +//Convert mm's to steps for the given axis +//Accurate to 1/1000 mm + +int32_t mmToSteps(float mm, enum Axis axis) { + //Multiply mm by 1000 to avoid floating point errors + int64_t intmm = (int64_t)(mm * 1000.0); + + //Calculate the number of steps + int64_t ret = intmm * axisStepsPerMM[axis]; + + //Divide the number of steps by the fixed precision and + //mm 1000; + for (uint8_t i=0 ; i < STEPS_PER_MM_PRECISION; i ++ ) + ret /= 10; + ret /= 1000; + + return (int32_t)ret; +} + +//Convert steps to mm's +//As accurate as floating point is + +float stepsToMM(int32_t steps, enum Axis axis) { + //Convert axisStepsPerMM to a float + float aspmf = (float)axisStepsPerMM[axis]; + for (uint8_t i=0 ; i < STEPS_PER_MM_PRECISION; i ++ ) + aspmf /= 10.0; + return (float)steps / aspmf; +} void strcat(char *buf, const char* str) { @@ -279,7 +321,7 @@ void JogMode::reset() { uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0); jogDistance = (enum distance_t)((jogModeSettings >> 1 ) & 0x07); - if ( jogDistance > DISTANCE_CONT ) jogDistance = DISTANCE_SHORT; + if ( jogDistance > DISTANCE_CONT ) jogDistance = DISTANCE_0_1MM; distanceChanged = false; lastDirectionButtonPressed = (ButtonArray::ButtonName)0; @@ -297,9 +339,9 @@ void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar jog3_user[] = "X V X (mode)"; const static PROGMEM prog_uchar jog4_user[] = " Y Z-"; - const static PROGMEM prog_uchar distanceShort[] = "SHORT"; - const static PROGMEM prog_uchar distanceLong[] = "LONG"; - const static PROGMEM prog_uchar distanceCont[] = "CONT"; + const static PROGMEM prog_uchar distance0_1mm[] = ".1mm"; + const static PROGMEM prog_uchar distance1mm[] = "1mm"; + const static PROGMEM prog_uchar distanceCont[] = "Cont.."; if ( userViewModeChanged ) userViewMode = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0) & 0x01; @@ -309,11 +351,13 @@ void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeFromPgmspace(jog1); switch (jogDistance) { - case DISTANCE_SHORT: - lcd.writeFromPgmspace(distanceShort); + case DISTANCE_0_1MM: + lcd.write(0xF3); //Write tilde + lcd.writeFromPgmspace(distance0_1mm); break; - case DISTANCE_LONG: - lcd.writeFromPgmspace(distanceLong); + case DISTANCE_1MM: + lcd.write(0xF3); //Write tilde + lcd.writeFromPgmspace(distance1mm); break; case DISTANCE_CONT: lcd.writeFromPgmspace(distanceCont); @@ -340,7 +384,10 @@ void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( lastDirectionButtonPressed ) { if (interface::isButtonPressed(lastDirectionButtonPressed)) JogMode::notifyButtonPressed(lastDirectionButtonPressed); - else lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + else { + lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + steppers::abort(); + } } } } @@ -349,45 +396,46 @@ void JogMode::jog(ButtonArray::ButtonName direction) { Point position = steppers::getPosition(); int32_t interval = 2000; - int32_t steps; + float speed; //In mm's if ( jogDistance == DISTANCE_CONT ) interval = 1000; switch(jogDistance) { - case DISTANCE_SHORT: - steps = 20; + case DISTANCE_0_1MM: + speed = 0.1; //0.1mm break; - case DISTANCE_LONG: - steps = 200; + case DISTANCE_1MM: + speed = 1.0; //1mm break; case DISTANCE_CONT: - steps = 50; + speed = 1.5; //1.5mm break; } + //Reverse direction of X and Y if we're in User View Mode and //not model mode - uint32_t vMode = 1; - if ( userViewMode ) vMode = -1;; + int32_t vMode = 1; + if ( userViewMode ) vMode = -1; switch(direction) { case ButtonArray::XMINUS: - position[0] -= vMode * steps; + position[0] -= vMode * mmToSteps(speed,AXIS_X); break; case ButtonArray::XPLUS: - position[0] += vMode * steps; + position[0] += vMode * mmToSteps(speed,AXIS_X); break; case ButtonArray::YMINUS: - position[1] -= vMode * steps; + position[1] -= vMode * mmToSteps(speed,AXIS_Y); break; case ButtonArray::YPLUS: - position[1] += vMode * steps; + position[1] += vMode * mmToSteps(speed,AXIS_Y); break; case ButtonArray::ZMINUS: - position[2] -= steps; + position[2] -= mmToSteps(speed,AXIS_Z); break; case ButtonArray::ZPLUS: - position[2] += steps; + position[2] += mmToSteps(speed,AXIS_Z); break; } @@ -406,14 +454,14 @@ void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { case ButtonArray::OK: switch(jogDistance) { - case DISTANCE_SHORT: - jogDistance = DISTANCE_LONG; + case DISTANCE_0_1MM: + jogDistance = DISTANCE_1MM; break; - case DISTANCE_LONG: + case DISTANCE_1MM: jogDistance = DISTANCE_CONT; break; case DISTANCE_CONT: - jogDistance = DISTANCE_SHORT; + jogDistance = DISTANCE_0_1MM; break; } distanceChanged = true; @@ -537,6 +585,9 @@ void ExtruderMode::extrude(seconds_t seconds, bool overrideTempCheck) { int32_t interval = (int32_t)(60L * 1000000L) / (int32_t)((float)(200 * 8) * rpm); int16_t stepsPerSecond = (int16_t)((200.0 * 8.0 * rpm) / 60.0); + //50.235479 is ToM stepper extruder speed, we use this as a baseline + stepsPerSecond = (int16_t)((float)stepsPerSecond * stepsToMM((int32_t)50.235479, AXIS_A)); + if ( seconds == 0 ) steppers::abort(); else { position[3] += seconds * stepsPerSecond; @@ -694,6 +745,7 @@ void ExtruderSetRpmScreen::notifyButtonPressed(ButtonArray::ButtonName button) { interface::popScreen(); break; case ButtonArray::ZERO: + break; case ButtonArray::OK: eeprom_write_byte((uint8_t *)eeprom::EXTRUDE_RPM, rpm); interface::popScreen(); @@ -877,6 +929,7 @@ void MoodLightSetRGBScreen::notifyButtonPressed(ButtonArray::ButtonName button) interface::popScreen(); break; case ButtonArray::ZERO: + break; case ButtonArray::OK: if ( inputMode < 2 ) { inputMode ++; @@ -1096,7 +1149,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeFromPgmspace(platform_temp); lcd.setCursor(15,3); - if ( command::getPauseAtZPos() == 0.0 ) lcd.write(' '); + if ( command::getPauseAtZPos() == 0 ) lcd.write(' '); else lcd.write('*'); } @@ -1144,7 +1197,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { } lcd.setCursor(15,3); - if ( command::getPauseAtZPos() == 0.0 ) lcd.write(' '); + if ( command::getPauseAtZPos() == 0 ) lcd.write(' '); else lcd.write('*'); break; case 4: @@ -1227,8 +1280,8 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { Point position = steppers::getPosition(); - //Divide by 200m because there are 200 steps per mm - lcd.writeFloat((float)position[2] / 200.0, 3); + //Divide by the axis steps to mm's + lcd.writeFloat(stepsToMM(position[2], AXIS_Z), 3); lcd.writeFromPgmspace(zpos_mm); break; @@ -1499,10 +1552,31 @@ void CancelBuildMenu::handleSelect(uint8_t index) { lind ++; } +int64_t MainMenu::checkAndGetEepromDefault(const uint16_t location, const int64_t default_value) { + int64_t value = eeprom::getEepromInt64(location, default_value); + + if (( value <= STEPS_PER_MM_LOWER_LIMIT ) || ( value >= STEPS_PER_MM_UPPER_LIMIT )) { + eeprom::putEepromInt64(location, default_value); + + //Just to be on the safe side + value = eeprom::getEepromInt64(location, default_value); + } + + return value; +} MainMenu::MainMenu() { - itemCount = 16; + itemCount = 17; reset(); + + //Read in the axisStepsPerMM, we'll need these for various firmware functions later on + cli(); + axisStepsPerMM[AXIS_X] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_X, STEPS_PER_MM_X_DEFAULT); + axisStepsPerMM[AXIS_Y] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_Y, STEPS_PER_MM_Y_DEFAULT); + axisStepsPerMM[AXIS_Z] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_Z, STEPS_PER_MM_Z_DEFAULT); + axisStepsPerMM[AXIS_A] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_A, STEPS_PER_MM_A_DEFAULT); + axisStepsPerMM[AXIS_B] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_B, STEPS_PER_MM_B_DEFAULT); + sei(); } void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { @@ -1520,6 +1594,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar calibrate[] = "Calibrate"; const static PROGMEM prog_uchar homeOffsets[] = "Home Offsets"; const static PROGMEM prog_uchar endStops[] = "Test End Stops"; + const static PROGMEM prog_uchar stepsPerMm[] = "Axis Steps:mm"; const static PROGMEM prog_uchar versions[] = "Version"; const static PROGMEM prog_uchar snake[] = "Snake Game"; @@ -1567,9 +1642,12 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(endStops); break; case 14: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(stepsPerMm); break; case 15: + lcd.writeFromPgmspace(versions); + break; + case 16: lcd.writeFromPgmspace(snake); break; } @@ -1635,10 +1713,14 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&testEndStopsMode); break; case 14: + // Show steps per mm menu + interface::pushScreen(&stepsPerMMMode); + break; + case 15: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 15: + case 16: // Show build from SD screen interface::pushScreen(&snake); break; @@ -1836,6 +1918,7 @@ void Tool0TempSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) { interface::popScreen(); break; case ButtonArray::ZERO: + break; case ButtonArray::OK: eeprom_write_byte((uint8_t*)eeprom::TOOL0_TEMP,value); interface::popScreen(); @@ -1902,6 +1985,7 @@ void PlatformTempSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) interface::popScreen(); break; case ButtonArray::ZERO: + break; case ButtonArray::OK: eeprom_write_byte((uint8_t*)eeprom::PLATFORM_TEMP,value); interface::popScreen(); @@ -2052,26 +2136,31 @@ void HomeAxisMode::update(LiquidCrystal& lcd, bool forceRedraw) { void HomeAxisMode::home(ButtonArray::ButtonName direction) { uint8_t axis = 0; bool maximums; + float interval = 2000.0; switch(direction) { case ButtonArray::XMINUS: case ButtonArray::XPLUS: axis = 0x01; maximums = false; + interval *= stepsToMM((int32_t)47.06, AXIS_X); //Use ToM as baseline break; case ButtonArray::YMINUS: case ButtonArray::YPLUS: axis = 0x02; maximums = false; + interval *= stepsToMM((int32_t)47.06, AXIS_Y); //Use ToM as baseline break; case ButtonArray::ZMINUS: case ButtonArray::ZPLUS: axis = 0x04; maximums = true; + interval /= 4.0; //Speed up Z + interval *= stepsToMM((int32_t)200.0, AXIS_Z); //Use ToM as baseline break; } - steppers::startHoming(maximums, axis, (uint32_t)2000); + steppers::startHoming(maximums, axis, (uint32_t)interval); } void HomeAxisMode::notifyButtonPressed(ButtonArray::ButtonName button) { @@ -2212,29 +2301,29 @@ void PauseMode::reset() { } void PauseMode::jog(ButtonArray::ButtonName direction) { - uint8_t steps = 50; bool extrude = false; int32_t interval = 1000; + float speed = 1.5; //In mm's Point position = steppers::getPosition(); switch(direction) { case ButtonArray::XMINUS: - position[0] -= steps; + position[0] -= mmToSteps(speed, AXIS_X); break; case ButtonArray::XPLUS: - position[0] += steps; + position[0] += mmToSteps(speed, AXIS_X); break; case ButtonArray::YMINUS: - position[1] -= steps; + position[1] -= mmToSteps(speed, AXIS_Y); break; case ButtonArray::YPLUS: - position[1] += steps; + position[1] += mmToSteps(speed, AXIS_Y); break; case ButtonArray::ZMINUS: - position[2] -= steps; + position[2] -= mmToSteps(speed, AXIS_Z); break; case ButtonArray::ZPLUS: - position[2] += steps; + position[2] += mmToSteps(speed, AXIS_Z); break; case ButtonArray::OK: case ButtonArray::ZERO: @@ -2245,6 +2334,10 @@ void PauseMode::jog(ButtonArray::ButtonName direction) { interval = (int32_t)(60L * 1000000L) / (int32_t)((float)(200 * 8) * rpm); int16_t stepsPerSecond = (int16_t)((200.0 * 8.0 * rpm) / 60.0); + //50.235479 is ToM stepper extruder speed, we + //use this as a baseline + stepsPerSecond = (int16_t)((float)stepsPerSecond * stepsToMM((int32_t)50.235479, AXIS_A)); + //Handle reverse if ( direction == ButtonArray::OK ) stepsPerSecond *= -1; @@ -2263,13 +2356,14 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar waitForCurrentCommand[] = "Entering pause.."; const static PROGMEM prog_uchar retractFilament[] = "Retract Filament"; const static PROGMEM prog_uchar movingZ[] = "Moving Z up 2mm "; + const static PROGMEM prog_uchar movingY[] = "Moving Y 2mm "; const static PROGMEM prog_uchar leavingPaused[] = "Leaving pause.. "; - const static PROGMEM prog_uchar paused1[] = "Paused: "; + const static PROGMEM prog_uchar paused1[] = "Paused("; const static PROGMEM prog_uchar paused2[] = " Y+ Z+"; const static PROGMEM prog_uchar paused3[] = "X- Rev X+ (Fwd)"; const static PROGMEM prog_uchar paused4[] = " Y- Z-"; - int32_t interval = 2000; + int32_t interval = 1000; Point newPosition = pausedPosition; if (forceRedraw) lcd.clear(); @@ -2289,8 +2383,8 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { pausedPosition = steppers::getPosition(); newPosition = pausedPosition; - newPosition[3] += 50; //Retract the filament so we don't get blobs - steppers::setTarget(newPosition, interval / 4); + newPosition[3] += mmToSteps(1.0, AXIS_A); //Retract the filament so we don't get blobs + steppers::setTarget(newPosition, interval / 2); pauseState ++; break; @@ -2302,19 +2396,33 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { } break; - case 3: //Last command finished, record current position and move - //Z away from build - lcd.writeFromPgmspace(movingZ); - + case 3: //Last command finished, record position and move Y to dislodge filament + lcd.writeFromPgmspace(movingY); pausedPosition = steppers::getPosition(); newPosition = pausedPosition; - newPosition[2] += 2 * 200; //200 because of the number of steps per mm - steppers::setTarget(newPosition, interval / 4); + newPosition[1] += mmToSteps(4.0, AXIS_Y); + steppers::setTarget(newPosition, interval / 2); + + pauseState ++; + break; + + case 4: //Wait for the Y move to complete + if ( ! steppers::isRunning()) { + pauseState ++; + } + break; + + case 5: //Last command finished, move Z away from build + lcd.writeFromPgmspace(movingZ); + + newPosition = steppers::getPosition(); + newPosition[2] += mmToSteps(2.0, AXIS_Z); + steppers::setTarget(newPosition, interval / 2); pauseState ++; break; - case 4: //Wait for the Z move up to complete + case 6: //Wait for the Z move up to complete lcd.writeFromPgmspace(movingZ); if ( ! steppers::isRunning()) { pauseState ++; @@ -2322,7 +2430,11 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { //We write this here to avoid tieing up the processor //in the next state lcd.clear(); + lcd.writeFromPgmspace(paused1); + lcd.writeFloat(stepsToMM(pausedPosition[2], AXIS_Z), 3); + lcd.writeString("):"); + lcd.setCursor(0,1); lcd.writeFromPgmspace(paused2); lcd.setCursor(0,2); @@ -2332,22 +2444,22 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { } break; - case 5: //Buzz if we're a Pause@ZPos + case 7: //Buzz if we're a Pause@ZPos if ( autoPause ) Motherboard::getBoard().buzz(4, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); pauseState ++; break; - case 6: //We're now paused + case 8: //We're now paused break; - case 7: //Leaving paused, wait for any steppers to finish - if ( autoPause ) command::pauseAtZPos(0.0); + case 9: //Leaving paused, wait for any steppers to finish + if ( autoPause ) command::pauseAtZPos(0); lcd.clear(); lcd.writeFromPgmspace(leavingPaused); if ( ! steppers::isRunning()) pauseState ++; break; - case 8: //Return to original position + case 10://Return to original position lcd.writeFromPgmspace(leavingPaused); //The extruders may have moved, so it doesn't make sense @@ -2360,7 +2472,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { pauseState ++; break; - case 9: //Wait for return to original position + case 11://Wait for return to original position lcd.writeFromPgmspace(leavingPaused); if ( ! steppers::isRunning()) { pauseState = 0; @@ -2374,23 +2486,26 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( lastDirectionButtonPressed ) { if (interface::isButtonPressed(lastDirectionButtonPressed)) jog(lastDirectionButtonPressed); - else lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + else { + lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + steppers::abort(); + } } } void PauseMode::notifyButtonPressed(ButtonArray::ButtonName button) { - if ( button == ButtonArray::CANCEL ) { - if ( pauseState == 6 ) pauseState ++; + if ( pauseState == 8 ) { + if ( button == ButtonArray::CANCEL ) pauseState ++; + else jog(button); } - else jog(button); } void PauseAtZPosScreen::reset() { - float currentPause = command::getPauseAtZPos(); - if ( currentPause == 0.0 ) { + int32_t currentPause = command::getPauseAtZPos(); + if ( currentPause == 0 ) { Point position = steppers::getPosition(); - pauseAtZPos = (float)position[2] / 200.0; - } else pauseAtZPos = currentPause; + pauseAtZPos = stepsToMM(position[2], AXIS_Z); + } else pauseAtZPos = stepsToMM(currentPause, AXIS_Z); } void PauseAtZPosScreen::update(LiquidCrystal& lcd, bool forceRedraw) { @@ -2416,10 +2531,11 @@ void PauseAtZPosScreen::update(LiquidCrystal& lcd, bool forceRedraw) { void PauseAtZPosScreen::notifyButtonPressed(ButtonArray::ButtonName button) { switch (button) { - case ButtonArray::OK: case ButtonArray::ZERO: + break; + case ButtonArray::OK: //Set the pause - command::pauseAtZPos(pauseAtZPos); + command::pauseAtZPos(mmToSteps(pauseAtZPos, AXIS_Z)); case ButtonArray::CANCEL: interface::popScreen(); interface::popScreen(); @@ -2587,25 +2703,30 @@ void CalibrateMode::update(LiquidCrystal& lcd, bool forceRedraw) { //Some states are changed when something completes, in which case we do it here uint8_t axes; + float interval = 2000.0; + switch(calibrationState) { case CS_HOME_Z: //Declare current position to be x=0, y=0, z=0, a=0, b=0 steppers::definePosition(Point(0,0,0,0,0)); - steppers::startHoming(true, 0x04, (uint32_t)2000); + interval *= stepsToMM((int32_t)200.0, AXIS_Z); //Use ToM as baseline + steppers::startHoming(true, 0x04, (uint32_t)interval); calibrationState = CS_HOME_Z_WAIT; break; case CS_HOME_Z_WAIT: if ( ! steppers::isHoming() ) calibrationState = CS_HOME_Y; break; case CS_HOME_Y: - steppers::startHoming(false, 0x02, (uint32_t)2000); + interval *= stepsToMM((int32_t)47.06, AXIS_Y); //Use ToM as baseline + steppers::startHoming(false, 0x02, (uint32_t)interval); calibrationState = CS_HOME_Y_WAIT; break; case CS_HOME_Y_WAIT: if ( ! steppers::isHoming() ) calibrationState = CS_HOME_X; break; case CS_HOME_X: - steppers::startHoming(false, 0x01, (uint32_t)2000); + interval *= stepsToMM((int32_t)47.06, AXIS_X); //Use ToM as baseline + steppers::startHoming(false, 0x01, (uint32_t)interval); calibrationState = CS_HOME_X_WAIT; break; case CS_HOME_X_WAIT: @@ -2677,6 +2798,7 @@ void HomeOffsetsMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar message1z[] = "Z Offset(steps):"; const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; const static PROGMEM prog_uchar blank[] = " "; + const static PROGMEM prog_uchar mm[] = "mm"; if ( homeOffsetState != lastHomeOffsetState ) forceRedraw = true; @@ -2704,18 +2826,19 @@ void HomeOffsetsMode::update(LiquidCrystal& lcd, bool forceRedraw) { switch(homeOffsetState) { case HOS_OFFSET_X: - position = (float)homePosition[0]; + position = stepsToMM(homePosition[0], AXIS_X); break; case HOS_OFFSET_Y: - position = (float)homePosition[1]; + position = stepsToMM(homePosition[1], AXIS_Y); break; case HOS_OFFSET_Z: - position = (float)homePosition[2]; + position = stepsToMM(homePosition[2], AXIS_Z); break; } lcd.setCursor(0,1); - lcd.writeFloat((float)position, 0); + lcd.writeFloat((float)position, 3); + lcd.writeFromPgmspace(mm); lastHomeOffsetState = homeOffsetState; } @@ -2735,42 +2858,38 @@ void HomeOffsetsMode::notifyButtonPressed(ButtonArray::ButtonName button) { return; } - float currentPosition; uint8_t currentIndex = homeOffsetState - HOS_OFFSET_X; - currentPosition = (float)homePosition[currentIndex]; - switch (button) { case ButtonArray::CANCEL: interface::popScreen(); break; case ButtonArray::ZERO: + break; case ButtonArray::OK: if ( homeOffsetState == HOS_OFFSET_X ) homeOffsetState = HOS_OFFSET_Y; else if ( homeOffsetState == HOS_OFFSET_Y ) homeOffsetState = HOS_OFFSET_Z; break; case ButtonArray::ZPLUS: // increment more - currentPosition += 5.0; + homePosition[currentIndex] += 20; break; case ButtonArray::ZMINUS: // decrement more - currentPosition -= 5.0; + homePosition[currentIndex] -= 20; break; case ButtonArray::YPLUS: // increment less - currentPosition += 1; + homePosition[currentIndex] += 1; break; case ButtonArray::YMINUS: // decrement less - currentPosition -= 1.0; + homePosition[currentIndex] -= 1; break; case ButtonArray::XMINUS: case ButtonArray::XPLUS: break; } - - homePosition[currentIndex] = (int32_t)currentPosition; } void BuzzerSetRepeatsMode::reset() { @@ -2808,6 +2927,7 @@ void BuzzerSetRepeatsMode::notifyButtonPressed(ButtonArray::ButtonName button) { interface::popScreen(); break; case ButtonArray::ZERO: + break; case ButtonArray::OK: eeprom_write_byte((uint8_t *)eeprom::BUZZER_REPEATS, repeats); interface::popScreen(); @@ -2881,4 +3001,154 @@ void ExtruderFanMenu::handleSelect(uint8_t index) { } } +void StepsPerMMMode::reset() { + lastStepsPerMMState = SPM_NONE; + stepsPerMMState = SPM_SET_X; + cursorLocation = 0; + originalStepsPerMM = axisStepsPerMM[AXIS_X]; +} + +#define STEPS_PER_MM_INCREMENT 0.000001 + +void StepsPerMMMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1x[] = "X Steps per mm:"; + const static PROGMEM prog_uchar message1y[] = "Y Steps per mm:"; + const static PROGMEM prog_uchar message1z[] = "Z Steps per mm:"; + const static PROGMEM prog_uchar message1a[] = "A Steps per mm:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if ( stepsPerMMState != lastStepsPerMMState ) forceRedraw = true; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + switch(stepsPerMMState) { + case SPM_SET_X: + lcd.writeFromPgmspace(message1x); + break; + case SPM_SET_Y: + lcd.writeFromPgmspace(message1y); + break; + case SPM_SET_Z: + lcd.writeFromPgmspace(message1z); + break; + case SPM_SET_A: + lcd.writeFromPgmspace(message1a); + break; + } + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + int64_t spm = 0; + + switch(stepsPerMMState) { + case SPM_SET_X: + spm = axisStepsPerMM[AXIS_X]; + break; + case SPM_SET_Y: + spm = axisStepsPerMM[AXIS_Y]; + break; + case SPM_SET_Z: + spm = axisStepsPerMM[AXIS_Z]; + break; + case SPM_SET_A: + spm = axisStepsPerMM[AXIS_A]; + break; + } + + //Write the number + lcd.setCursor(0,1); + lcd.writeFixedPoint(spm, STEPS_PER_MM_PADDING, STEPS_PER_MM_PRECISION); + + //Draw the cursor + lcd.setCursor(cursorLocation,2); + lcd.write('^'); + + //Write a blank before and after the cursor if we're not at the ends + if ( cursorLocation >= 1 ) { + lcd.setCursor(cursorLocation-1, 2); + lcd.writeFromPgmspace(blank); + } + if ( cursorLocation < 15 ) { + lcd.setCursor(cursorLocation+1, 2); + lcd.writeFromPgmspace(blank); + } + + lastStepsPerMMState = stepsPerMMState; +} + +void StepsPerMMMode::notifyButtonPressed(ButtonArray::ButtonName button) { + int64_t spm; + uint16_t offset; + uint8_t currentIndex = stepsPerMMState - SPM_SET_X; + + spm = axisStepsPerMM[currentIndex]; + + //Calculate the increment based on the cursor location, allowing + //for the decimal point + int64_t increment = 1; + for (uint8_t i = (STEPS_PER_MM_PADDING + STEPS_PER_MM_PRECISION); i >= 0; i -- ) { + if ( i == cursorLocation ) break; + if ( i != STEPS_PER_MM_PADDING ) increment *= 10; + } + + //Don't increment if we're sitting on the decimcal point + if ( cursorLocation == STEPS_PER_MM_PADDING ) increment = 0; + + switch (button) { + case ButtonArray::CANCEL: + axisStepsPerMM[currentIndex] = originalStepsPerMM; + interface::popScreen(); + return; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + //Write the new steps per mm positions + offset = eeprom::STEPS_PER_MM_X + sizeof(int64_t) * currentIndex; + cli(); + eeprom::putEepromInt64(offset,axisStepsPerMM[currentIndex]); + + //Read it back in, because we could have floating point rounding happening + axisStepsPerMM[currentIndex] = eeprom::getEepromInt64(offset, 1); + sei(); + + if ( stepsPerMMState == SPM_SET_A ) { + interface::popScreen(); + } + else { + //Increment to the next index + stepsPerMMState = (enum StepsPerMMState)((uint8_t)stepsPerMMState + 1); + cursorLocation = 0; + originalStepsPerMM = axisStepsPerMM[currentIndex + 1]; + } + return; + case ButtonArray::YPLUS: + case ButtonArray::ZPLUS: + // increment + spm += increment; + break; + case ButtonArray::YMINUS: + case ButtonArray::ZMINUS: + // decrement + spm -= increment; + break; + case ButtonArray::XMINUS: + if ( cursorLocation > 0 ) cursorLocation --; + break; + case ButtonArray::XPLUS: + if ( cursorLocation < 15 ) cursorLocation ++; + break; + } + + //Hard limits + if ( spm >= STEPS_PER_MM_UPPER_LIMIT ) spm = STEPS_PER_MM_UPPER_LIMIT; + if ( spm <= STEPS_PER_MM_LOWER_LIMIT ) spm = STEPS_PER_MM_LOWER_LIMIT; + + axisStepsPerMM[currentIndex] = spm; +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index a56e5b9..3a9cd54 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -103,8 +103,8 @@ protected: class JogMode: public Screen { private: enum distance_t { - DISTANCE_SHORT = 0, - DISTANCE_LONG, + DISTANCE_0_1MM = 0, + DISTANCE_1MM, DISTANCE_CONT, }; @@ -563,6 +563,32 @@ protected: void handleSelect(uint8_t index); }; +class StepsPerMMMode: public Screen { +private: + enum StepsPerMMState { + SPM_NONE, + SPM_SET_X, + SPM_SET_Y, + SPM_SET_Z, + SPM_SET_A + }; + + enum StepsPerMMState stepsPerMMState, lastStepsPerMMState; + + uint8_t cursorLocation; + + int64_t originalStepsPerMM; + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -586,10 +612,13 @@ private: ExtruderFanMenu extruderFanMenu; CalibrateMode calibrateMode; HomeOffsetsMode homeOffsetsMode; + StepsPerMMMode stepsPerMMMode; TestEndStopsMode testEndStopsMode; VersionMode versionMode; MoodLightMode moodLightMode; SnakeMode snake; + + int64_t checkAndGetEepromDefault(const uint16_t location, const int64_t default_value); }; #endif From 293b234532dda59c8fc93c93cc1d7dfeb4c01854 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Sat, 21 Jan 2012 16:34:19 -0700 Subject: [PATCH 34/61] Implemented an Estimator to estimate the build time accurately when Building From SD Card. The build process also now provides the length of filament used. --- firmware/src/Motherboard/Command.cc | 227 ++++++++++++++++++++------ firmware/src/Motherboard/Command.hh | 9 + firmware/src/Motherboard/Host.cc | 14 +- firmware/src/Motherboard/Host.hh | 14 +- firmware/src/Motherboard/SDCard.cc | 11 ++ firmware/src/Motherboard/SDCard.hh | 3 + firmware/src/Motherboard/Steppers.hh | 4 +- firmware/src/shared/InterfaceBoard.cc | 1 + firmware/src/shared/Menu.cc | 191 ++++++++++++++++------ firmware/src/shared/Menu.hh | 3 +- 10 files changed, 359 insertions(+), 118 deletions(-) diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index 3c49efa..7baf488 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -43,6 +43,12 @@ volatile uint32_t recentCommandClock = 0; volatile uint32_t recentCommandTime = 0; volatile int32_t pauseZPos = 0; +bool estimating = false; +int64_t estimateTimeUs = 0; +int64_t filamentLength = 0; //This maybe pos or neg, but ABS it and all is good (in steps) + +Point lastPosition; + uint16_t getRemainingCapacity() { uint16_t sz; ATOMIC_BLOCK(ATOMIC_FORCEON) { @@ -122,7 +128,10 @@ Timeout tool_wait_timeout; void reset() { pauseAtZPos(0.0); + lastPosition = Point(0,0,0,0,0); command_buffer.reset(); + estimateTimeUs = 0; + filamentLength = 0; mode = READY; } @@ -178,13 +187,91 @@ bool isPlatformReady() { return false; } +int64_t getFilamentLength() { + if ( filamentLength < 0 ) return -filamentLength; + return filamentLength; +} + +int32_t estimateSeconds() { + return estimateTimeUs / 1000000; +} + +//Set the estimation mode +void setEstimation(bool on) { + //If we were estimating and we're switching to a build + //reset for the build + if (( estimating ) && ( ! on )) { + recentCommandClock = 0; + recentCommandTime = 0; + command_buffer.reset(); + sdcard::playbackRestart(); + } + + estimateTimeUs = 0; + filamentLength = 0; + estimating = on; +} + +void estimateDelay(uint32_t microseconds) { + estimateTimeUs += (int64_t)microseconds; +} + +void estimateDefinePosition(Point p) { + for ( uint8_t i = 0; i < AXIS_COUNT; i ++ ) lastPosition[i] = p[i]; +} + +int32_t estimateAbs(int32_t v) { + if ( v < 0 ) return v * -1; + return v; +} + +void estimateMoveTo(Point p, int32_t dda) { + //Calculate deltas + Point delta; + for ( uint8_t i = 0; i < AXIS_COUNT; i ++ ) delta[i] = estimateAbs(lastPosition[i] - p[i]); + + //Find max of the deltas + int32_t max = delta[0]; + for ( uint8_t i = 0; i < AXIS_COUNT; i ++ ) { + if ( delta[i] > max ) max = delta[i]; + } + + + estimateTimeUs += (int64_t)max * (int64_t)dda; + + if ( ! estimating ) { + filamentLength += (int64_t)(p[3] - lastPosition[3]); + filamentLength += (int64_t)(p[4] - lastPosition[4]); + } + + //Setup lastPosition as the current target + for ( uint8_t i = 0; i < AXIS_COUNT; i ++ ) lastPosition[i] = p[i]; +} + +void estimateMoveToNew(Point p, int32_t us, uint8_t relative) { + estimateTimeUs += (int64_t)us; + + //Set last, based on if we moved relative or not on that axis + for ( uint8_t i = 0; i < AXIS_COUNT; i ++ ) { + + if ( relative & (1 << i)) { + if (( ! estimating ) && (( i == 3 ) || ( i == 4 ))) + filamentLength += (int64_t)p[i]; + lastPosition[i] += p[i]; + } else { + if (( ! estimating ) && (( i == 3 ) || ( i == 4 ))) + filamentLength += (int64_t)(p[i] - lastPosition[i]); + lastPosition[i] = p[i]; + } + } +} // A fast slice for processing commands and refilling the stepper queue, etc. void runCommandSlice() { recentCommandClock ++; #ifdef HAS_MOOD_LIGHT - updateMoodStatus(); + if ( ! estimating ) updateMoodStatus(); #endif if (sdcard::isPlaying()) { @@ -192,15 +279,22 @@ void runCommandSlice() { command_buffer.push(sdcard::playbackNext()); } } - if (paused) { return; } + if ((paused) && ( ! estimating )) { return; } - //If PauseAtZPos is enabled, pause when we reach zpos - //0.005 because of float point rounding errors, we want - //to make sure we stop at the correct place - if (( pauseZPos != 0) && ( ! isPaused() ) && - ( steppers::getPosition()[2]) >= pauseZPos ) + //If we've reached Pause @ ZPos, then pause + if ((( pauseZPos != 0) && ( ! isPaused() ) && + ( steppers::getPosition()[2]) >= pauseZPos ) && ( ! estimating )) pause(true); + //If we're estimating, we don't need to wait for anything + if (( estimating ) && + (( mode == HOMING ) || + ( mode == MOVING ) || + ( mode == DELAY ) || + ( mode == WAIT_ON_TOOL ) || + ( mode == WAIT_ON_PLATFORM ))) + mode = READY; + if (mode == HOMING) { if (!steppers::isRunning()) { mode = READY; @@ -232,6 +326,7 @@ void runCommandSlice() { mode = READY; } } + Point p; if (mode == READY) { // process next command on the queue. if (command_buffer.getLength() > 0) { @@ -246,7 +341,8 @@ void runCommandSlice() { int32_t y = pop32(); int32_t z = pop32(); int32_t dda = pop32(); - steppers::setTarget(Point(x,y,z),dda); + estimateMoveTo(Point(x,y,z),dda); + if ( ! estimating ) steppers::setTarget(Point(x,y,z),dda); } } else if (command == HOST_CMD_QUEUE_POINT_EXT) { recentCommandTime = recentCommandClock; @@ -260,7 +356,8 @@ void runCommandSlice() { int32_t a = pop32(); int32_t b = pop32(); int32_t dda = pop32(); - steppers::setTarget(Point(x,y,z,a,b),dda); + estimateMoveTo(Point(x,y,z,a,b),dda); + if ( ! estimating ) steppers::setTarget(Point(x,y,z,a,b),dda); } } else if (command == HOST_CMD_QUEUE_POINT_NEW) { recentCommandTime = recentCommandClock; @@ -275,12 +372,14 @@ void runCommandSlice() { int32_t b = pop32(); int32_t us = pop32(); uint8_t relative = pop8(); - steppers::setTargetNew(Point(x,y,z,a,b),us,relative); + estimateMoveToNew(Point(x,y,z,a,b),us,relative); + if ( ! estimating ) steppers::setTargetNew(Point(x,y,z,a,b),us,relative); } } else if (command == HOST_CMD_CHANGE_TOOL) { if (command_buffer.getLength() >= 2) { command_buffer.pop(); // remove the command code - tool::setCurrentToolheadIndex(command_buffer.pop()); + uint8_t tIndex = command_buffer.pop(); + if ( ! estimating ) tool::setCurrentToolheadIndex(tIndex); } } else if (command == HOST_CMD_ENABLE_AXES) { recentCommandTime = recentCommandClock; @@ -290,7 +389,7 @@ void runCommandSlice() { bool enable = (axes & 0x80) != 0; for (int i = 0; i < STEPPER_COUNT; i++) { if ((axes & _BV(i)) != 0) { - steppers::enableAxis(i, enable); + if ( ! estimating ) steppers::enableAxis(i, enable); } } } @@ -301,7 +400,8 @@ void runCommandSlice() { int32_t x = pop32(); int32_t y = pop32(); int32_t z = pop32(); - steppers::definePosition(Point(x,y,z)); + estimateDefinePosition(Point(x,y,z)); + if ( ! estimating ) steppers::definePosition(Point(x,y,z)); } } else if (command == HOST_CMD_SET_POSITION_EXT) { // check for completion @@ -312,7 +412,8 @@ void runCommandSlice() { int32_t z = pop32(); int32_t a = pop32(); int32_t b = pop32(); - steppers::definePosition(Point(x,y,z,a,b)); + estimateDefinePosition(Point(x,y,z,a,b)); + if ( ! estimating ) steppers::definePosition(Point(x,y,z,a,b)); } } else if (command == HOST_CMD_DELAY) { if (command_buffer.getLength() >= 5) { @@ -320,7 +421,8 @@ void runCommandSlice() { command_buffer.pop(); // remove the command code // parameter is in milliseconds; timeouts need microseconds uint32_t microseconds = pop32() * 1000; - delay_timeout.start(microseconds); + estimateDelay(microseconds); + if ( ! estimating ) delay_timeout.start(microseconds); } } else if (command == HOST_CMD_FIND_AXES_MINIMUM || command == HOST_CMD_FIND_AXES_MAXIMUM) { @@ -332,9 +434,10 @@ void runCommandSlice() { bool direction = command == HOST_CMD_FIND_AXES_MAXIMUM; mode = HOMING; homing_timeout.start(timeout_s * 1000L * 1000L); - steppers::startHoming(command==HOST_CMD_FIND_AXES_MAXIMUM, - flags, - feedrate); + if ( ! estimating ) + steppers::startHoming(command==HOST_CMD_FIND_AXES_MAXIMUM, + flags, + feedrate); } } else if (command == HOST_CMD_WAIT_FOR_TOOL) { if (command_buffer.getLength() >= 6) { @@ -343,7 +446,7 @@ void runCommandSlice() { uint8_t currentToolIndex = command_buffer.pop(); uint16_t toolPingDelay = (uint16_t)pop16(); uint16_t toolTimeout = (uint16_t)pop16(); - tool_wait_timeout.start(toolTimeout*1000000L); + if ( ! estimating ) tool_wait_timeout.start(toolTimeout*1000000L); } } else if (command == HOST_CMD_WAIT_FOR_PLATFORM) { // FIXME: Almost equivalent to WAIT_FOR_TOOL @@ -353,7 +456,7 @@ void runCommandSlice() { uint8_t currentToolIndex = command_buffer.pop(); uint16_t toolPingDelay = (uint16_t)pop16(); uint16_t toolTimeout = (uint16_t)pop16(); - tool_wait_timeout.start(toolTimeout*1000000L); + if ( ! estimating ) tool_wait_timeout.start(toolTimeout*1000000L); } } else if (command == HOST_CMD_STORE_HOME_POSITION) { @@ -362,15 +465,17 @@ void runCommandSlice() { command_buffer.pop(); uint8_t axes = pop8(); - // Go through each axis, and if that axis is specified, read it's value, - // then record it to the eeprom. - for (uint8_t i = 0; i < STEPPER_COUNT; i++) { - if ( axes & (1 << i) ) { - uint16_t offset = eeprom::AXIS_HOME_POSITIONS + 4*i; - uint32_t position = steppers::getPosition()[i]; - cli(); - eeprom_write_block(&position, (void*) offset, 4); - sei(); + if ( ! estimating ) { + // Go through each axis, and if that axis is specified, read it's value, + // then record it to the eeprom. + for (uint8_t i = 0; i < STEPPER_COUNT; i++) { + if ( axes & (1 << i) ) { + uint16_t offset = eeprom::AXIS_HOME_POSITIONS + 4*i; + uint32_t position = steppers::getPosition()[i]; + cli(); + eeprom_write_block(&position, (void*) offset, 4); + sei(); + } } } } @@ -379,8 +484,10 @@ void runCommandSlice() { if (command_buffer.getLength() >= 2) { command_buffer.pop(); uint8_t axes = pop8(); - - Point newPoint = steppers::getPosition(); + + Point newPoint; + if ( estimating ) newPoint = lastPosition; + else newPoint = steppers::getPosition(); for (uint8_t i = 0; i < STEPPER_COUNT; i++) { if ( axes & (1 << i) ) { @@ -391,7 +498,8 @@ void runCommandSlice() { } } - steppers::definePosition(newPoint); + estimateDefinePosition(newPoint); + if ( ! estimating ) steppers::definePosition(newPoint); } } else if (command == HOST_CMD_TOOL_COMMAND) { @@ -399,20 +507,28 @@ void runCommandSlice() { uint8_t payload_length = command_buffer[3]; if (command_buffer.getLength() >= 4+payload_length) { // command is ready - if (tool::getLock()) { - OutPacket& out = tool::getOutPacket(); - out.reset(); - command_buffer.pop(); // remove the command code - out.append8(command_buffer.pop()); // copy tool index - out.append8(command_buffer.pop()); // copy command code - int len = pop8(); // get payload length - for (int i = 0; i < len; i++) { - out.append8(command_buffer.pop()); + if ( estimating ) { + command_buffer.pop(); + command_buffer.pop(); + command_buffer.pop(); + int len = pop8(); + for (int i = 0; i < len; i++) command_buffer.pop(); + } else { + if (tool::getLock()) { + OutPacket& out = tool::getOutPacket(); + out.reset(); + command_buffer.pop(); // remove the command code + out.append8(command_buffer.pop()); // copy tool index + out.append8(command_buffer.pop()); // copy command code + int len = pop8(); // get payload length + for (int i = 0; i < len; i++) { + out.append8(command_buffer.pop()); + } + // we don't care about the response, so we can release + // the lock after we initiate the transfer + tool::startTransaction(); + tool::releaseLock(); } - // we don't care about the response, so we can release - // the lock after we initiate the transfer - tool::startTransaction(); - tool::releaseLock(); } } } @@ -426,7 +542,8 @@ void runCommandSlice() { int32_t fadeSpeed = pop32(); int32_t writeToEeprom = pop32(); #ifdef HAS_MOOD_LIGHT - Motherboard::getBoard().MoodLightSetRGBColor((uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)fadeSpeed, (uint8_t)writeToEeprom); + if ( ! estimating ) + Motherboard::getBoard().MoodLightSetRGBColor((uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)fadeSpeed, (uint8_t)writeToEeprom); #endif } } else if (command == HOST_CMD_MOOD_LIGHT_SET_HSB ) { @@ -438,7 +555,7 @@ void runCommandSlice() { int32_t b = pop32(); int32_t fadeSpeed = pop32(); #ifdef HAS_MOOD_LIGHT - Motherboard::getBoard().MoodLightSetHSBColor((uint8_t)h, (uint8_t)s, (uint8_t)b, (uint8_t)fadeSpeed); + if ( ! estimating ) Motherboard::getBoard().MoodLightSetHSBColor((uint8_t)h, (uint8_t)s, (uint8_t)b, (uint8_t)fadeSpeed); #endif } } else if (command == HOST_CMD_MOOD_LIGHT_PLAY_SCRIPT ) { @@ -448,16 +565,18 @@ void runCommandSlice() { int32_t scriptId = pop32(); int32_t writeToEeprom = pop32(); #ifdef HAS_MOOD_LIGHT - Motherboard::getBoard().MoodLightPlayScript((uint8_t)scriptId, (uint8_t)writeToEeprom); + if ( ! estimating ) Motherboard::getBoard().MoodLightPlayScript((uint8_t)scriptId, (uint8_t)writeToEeprom); #endif } } else if (command == HOST_CMD_BUZZER_REPEATS ) { // check for completion if (command_buffer.getLength() >= 2) { command_buffer.pop(); // remove the command code - cli(); - eeprom_write_byte((uint8_t*)eeprom::BUZZER_REPEATS, pop8()); - sei(); + if ( ! estimating ) { + cli(); + eeprom_write_byte((uint8_t*)eeprom::BUZZER_REPEATS, pop8()); + sei(); + } } } else if (command == HOST_CMD_BUZZER_BUZZ ) { // check for completion @@ -467,8 +586,10 @@ void runCommandSlice() { uint8_t duration = pop8(); uint8_t repeats = pop8(); #ifdef HAS_BUZZER - if ( buzzes == 0 ) Motherboard::getBoard().stopBuzzer(); - else Motherboard::getBoard().buzz(buzzes, duration, repeats); + if ( ! estimating ) { + if ( buzzes == 0 ) Motherboard::getBoard().stopBuzzer(); + else Motherboard::getBoard().buzz(buzzes, duration, repeats); + } #endif } } else { diff --git a/firmware/src/Motherboard/Command.hh b/firmware/src/Motherboard/Command.hh index 799f4e6..1a761b4 100644 --- a/firmware/src/Motherboard/Command.hh +++ b/firmware/src/Motherboard/Command.hh @@ -49,6 +49,15 @@ void pauseAtZPos(int32_t zpos); /// \return the z position set for pausing (in steps), otherwise 0 int32_t getPauseAtZPos(); +/// Returns the length of filament extruded (in steps) +int64_t getFilamentLength(); + +//Returns the number of seconds estimated +int32_t estimateSeconds(); + +//Set the estimation mode +void setEstimation(bool on); + /// Check the remaining capacity of the command buffer /// \return Amount of space left in the buffer, in bytes uint16_t getRemainingCapacity(); diff --git a/firmware/src/Motherboard/Host.cc b/firmware/src/Motherboard/Host.cc index 7b6a962..6e7e450 100644 --- a/firmware/src/Motherboard/Host.cc +++ b/firmware/src/Motherboard/Host.cc @@ -260,7 +260,7 @@ inline void handlePlayback(const InPacket& from_host, OutPacket& to_host) { } buildName[MAX_FILE_LEN-1] = '\0'; - uint8_t response = startBuildFromSD(); + uint8_t response = startBuildFromSD(false); to_host.append8(response); } @@ -467,7 +467,8 @@ bool processQueryPacket(const InPacket& from_host, OutPacket& to_host) { case HOST_CMD_RESET: // TODO: This is fishy. if (currentState == HOST_STATE_BUILDING - || currentState == HOST_STATE_BUILDING_FROM_SD) { + || currentState == HOST_STATE_BUILDING_FROM_SD + || currentState == HOST_STATE_ESTIMATING_FROM_SD) { stopBuild(); } @@ -559,7 +560,7 @@ HostState getHostState() { return currentState; } -sdcard::SdErrorCode startBuildFromSD() { +sdcard::SdErrorCode startBuildFromSD(bool estimateFirst) { sdcard::SdErrorCode e; // Attempt to start build @@ -569,7 +570,8 @@ sdcard::SdErrorCode startBuildFromSD() { return e; } - currentState = HOST_STATE_BUILDING_FROM_SD; + if ( estimateFirst ) currentState = HOST_STATE_ESTIMATING_FROM_SD; + else currentState = HOST_STATE_BUILDING_FROM_SD; return e; } @@ -584,6 +586,10 @@ bool isBuildComplete() { return false; } +void setHostStateBuildingFromSD() { + if ( currentState == HOST_STATE_ESTIMATING_FROM_SD ) currentState = HOST_STATE_BUILDING_FROM_SD; +} + } /* footnote 1: due to a protocol change, replicatiorG 0026 and newer can ONLY work with diff --git a/firmware/src/Motherboard/Host.hh b/firmware/src/Motherboard/Host.hh index dba0f3d..afd2fb4 100644 --- a/firmware/src/Motherboard/Host.hh +++ b/firmware/src/Motherboard/Host.hh @@ -32,10 +32,11 @@ const int MAX_FILE_LEN = MAX_PACKET_PAYLOAD-1; /// The host can be in any of these four states. enum HostState { - HOST_STATE_READY = 0, - HOST_STATE_BUILDING = 1, - HOST_STATE_BUILDING_FROM_SD = 2, - HOST_STATE_ERROR = 3 + HOST_STATE_READY = 0, + HOST_STATE_BUILDING = 1, + HOST_STATE_BUILDING_FROM_SD = 2, + HOST_STATE_ERROR = 3, + HOST_STATE_ESTIMATING_FROM_SD = 4 }; /// Run the host slice. This function handles incoming packets and host resets. @@ -58,13 +59,16 @@ HostState getHostState(); /// Start a build from SD card. The build name should be set by overwriting /// the value of buildName, provided by #getBuildName(). /// \return True if build started successfully. -sdcard::SdErrorCode startBuildFromSD(); +sdcard::SdErrorCode startBuildFromSD(bool estimateFirst); /// Stop the current build void stopBuild(); /// Returns true if the build is completed bool isBuildComplete(); + +//Sets the host state to building (if it's estimating) +void setHostStateBuildingFromSD(); } #endif // HOST_HH_ diff --git a/firmware/src/Motherboard/SDCard.cc b/firmware/src/Motherboard/SDCard.cc index a2b2688..b6cfbd3 100644 --- a/firmware/src/Motherboard/SDCard.cc +++ b/firmware/src/Motherboard/SDCard.cc @@ -312,6 +312,17 @@ float getPercentPlayed() { else return percentPlayed; } +void playbackRestart() { + capturedBytes = 0L; + playedBytes = 0L; + playing = true; + + int32_t offset = 0; + fat_seek_file(file, &offset, FAT_SEEK_SET); + + fetchNextByte(); +} + void playbackRewind(uint8_t bytes) { int32_t offset = -((int32_t)bytes); fat_seek_file(file, &offset, FAT_SEEK_CUR); diff --git a/firmware/src/Motherboard/SDCard.hh b/firmware/src/Motherboard/SDCard.hh index fd84e6b..1316362 100644 --- a/firmware/src/Motherboard/SDCard.hh +++ b/firmware/src/Motherboard/SDCard.hh @@ -99,6 +99,9 @@ namespace sdcard { /// \return The next byre in the file. uint8_t playbackNext(); + /// Rewinds a play back to the beginning + void playbackRestart(); + /// Rewind the given number of bytes in the input stream. /// \param[in] bytes Number of bytes to rewind diff --git a/firmware/src/Motherboard/Steppers.hh b/firmware/src/Motherboard/Steppers.hh index 84f8303..e8ede59 100644 --- a/firmware/src/Motherboard/Steppers.hh +++ b/firmware/src/Motherboard/Steppers.hh @@ -63,12 +63,12 @@ namespace steppers { /// Instruct the stepper subsystem to move the machine to the /// given position. /// \param[in] target Position to move to - /// \param[in] ms Duration of the move, in milliseconds + /// \param[in] us Duration of the move, in microseconds /// \param[in] relative Bitfield specifying whether each axis should /// interpret the new position as absolute or /// relative. void setTargetNew(const Point& target, - int32_t ms, + int32_t us, uint8_t relative =0); /// Home one or more axes diff --git a/firmware/src/shared/InterfaceBoard.cc b/firmware/src/shared/InterfaceBoard.cc index f6dc7d8..2a5614f 100644 --- a/firmware/src/shared/InterfaceBoard.cc +++ b/firmware/src/shared/InterfaceBoard.cc @@ -55,6 +55,7 @@ void InterfaceBoard::doUpdate() { switch(host::getHostState()) { case host::HOST_STATE_BUILDING: case host::HOST_STATE_BUILDING_FROM_SD: + case host::HOST_STATE_ESTIMATING_FROM_SD: if (!building) { pushScreen(buildScreen); building = true; diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 107f5f8..a00a4df 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -33,6 +33,8 @@ int16_t overrideExtrudeSeconds = 0; +bool estimatingBuild = false; + Point pausedPosition, homePosition; //Stored using STEPS_PER_MM_PRECISION @@ -1094,11 +1096,11 @@ void MonitorMode::reset() { updatePhase = 0; buildTimePhase = 0; buildComplete = false; - extruderStartSeconds = 0.0; lastElapsedSeconds = 0.0; pausePushLockout = false; pauseMode.autoPause = false; buildCompleteBuzzPlayed = false; + overrideForceRedraw = false; } @@ -1108,11 +1110,15 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar elapsed_time[] = "Elapsed: 0h00m"; const static PROGMEM prog_uchar completed_percent[] = "Completed: 0% "; const static PROGMEM prog_uchar time_left[] = "TimeLeft: 0h00m"; - const static PROGMEM prog_uchar time_left_calc[] = " calc.."; - const static PROGMEM prog_uchar time_left_1min[] = " <1min"; + const static PROGMEM prog_uchar duration[] = "Duration: 0h00m"; + const static PROGMEM prog_uchar time_left_secs[] = "secs"; const static PROGMEM prog_uchar time_left_none[] = " none"; const static PROGMEM prog_uchar zpos[] = "ZPos: "; const static PROGMEM prog_uchar zpos_mm[] = "mm"; + const static PROGMEM prog_uchar estimate2[] = "Estimating: 0%"; + const static PROGMEM prog_uchar estimate3[] = " (skip)"; + const static PROGMEM prog_uchar estimate4[] = "Duration: 0h00m"; + const static PROGMEM prog_uchar filament[] = "Filament:0.00m "; char buf[17]; if ( command::isPaused() ) { @@ -1124,7 +1130,10 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { } } else pausePushLockout = false; - if (forceRedraw) { + if ( host::getHostState() != host::HOST_STATE_ESTIMATING_FROM_SD ) + estimatingBuild = false; + + if ((forceRedraw) || (overrideForceRedraw)) { lcd.clear(); lcd.setCursor(0,0); switch(host::getHostState()) { @@ -1133,24 +1142,65 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { break; case host::HOST_STATE_BUILDING: case host::HOST_STATE_BUILDING_FROM_SD: + case host::HOST_STATE_ESTIMATING_FROM_SD: lcd.writeString(host::getBuildName()); lcd.setCursor(0,1); - lcd.writeFromPgmspace(completed_percent); + if ( estimatingBuild ) { + lcd.writeFromPgmspace(estimate2); + lcd.setCursor(0,2); + lcd.writeFromPgmspace(estimate3); + lcd.setCursor(0,3); + lcd.writeFromPgmspace(estimate4); + } else { + lcd.writeFromPgmspace(completed_percent); + } break; case host::HOST_STATE_ERROR: lcd.writeString("error!"); break; } - lcd.setCursor(0,2); - lcd.writeFromPgmspace(extruder_temp); + if ( ! estimatingBuild ) { + lcd.setCursor(0,2); + lcd.writeFromPgmspace(extruder_temp); - lcd.setCursor(0,3); - lcd.writeFromPgmspace(platform_temp); + lcd.setCursor(0,3); + lcd.writeFromPgmspace(platform_temp); - lcd.setCursor(15,3); - if ( command::getPauseAtZPos() == 0 ) lcd.write(' '); - else lcd.write('*'); + lcd.setCursor(15,3); + if ( command::getPauseAtZPos() == 0 ) lcd.write(' '); + else lcd.write('*'); + } + } + + overrideForceRedraw = false; + + //Display estimating stats + if ( estimatingBuild ) { + //Write out the % estimated + lcd.setCursor(12,1); + buf[0] = '\0'; + appendUint8(buf, sizeof(buf), (uint8_t)sdcard::getPercentPlayed()); + strcat(buf, "%"); + lcd.writeString(buf); + + //Write out the time calculated + buf[0] = '\0'; + lcd.setCursor(9,3); + appendTime(buf, sizeof(buf), (uint32_t)command::estimateSeconds()); + lcd.writeString(buf); + + //Check for estimate finished, and switch states to building + if (( ! sdcard::playbackHasNext() ) && ( command::isEmpty())) { + //Store the estimate seconds + buildDuration = command::estimateSeconds(); + host::setHostStateBuildingFromSD(); + command::setEstimation(false); + overrideForceRedraw = true; + estimatingBuild = false; + } + + return; } OutPacket responsePacket; @@ -1211,24 +1261,38 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { Motherboard::getBoard().buzz(2, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); } - float secs; - //Holding the zero button stops rotation - if ( ! interface::isButtonPressed(ButtonArray::OK) ) buildTimePhase ++; + if ( ! interface::isButtonPressed(ButtonArray::OK) ) { + buildTimePhase ++; + + //Skip Time Left if we skipped the estimation + if (( buildDuration == 0 ) && ( buildTimePhase == 2 )) buildTimePhase ++; + } - if ( buildTimePhase >= 4 ) buildTimePhase = 0; + if ( buildTimePhase >= 5 ) buildTimePhase = 0; + + float secs; + int32_t tsecs; + Point position; + uint8_t precision; + float filamentUsed; + float completedPercent; switch (buildTimePhase) { - case 0: + case 0: //Completed Percent lcd.setCursor(0,1); lcd.writeFromPgmspace(completed_percent); lcd.setCursor(11,1); buf[0] = '\0'; - appendUint8(buf, sizeof(buf), (uint8_t)sdcard::getPercentPlayed()); + + if ( buildDuration == 0 ) completedPercent = sdcard::getPercentPlayed(); + else completedPercent = ((float)command::estimateSeconds() / (float)buildDuration) * 100.0; + + appendUint8(buf, sizeof(buf), (uint8_t)completedPercent); strcat(buf, "% "); lcd.writeString(buf); break; - case 1: + case 1: //Elapsed Time lcd.setCursor(0,1); lcd.writeFromPgmspace(elapsed_time); lcd.setCursor(9,1); @@ -1242,56 +1306,62 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { appendTime(buf, sizeof(buf), (uint32_t)secs); lcd.writeString(buf); break; - case 2: + case 2: // Time Left lcd.setCursor(0,1); - lcd.writeFromPgmspace(time_left); + if ( command::getFilamentLength() >= 1 ) lcd.writeFromPgmspace(time_left); + else lcd.writeFromPgmspace(duration); lcd.setCursor(9,1); - if (( sdcard::getPercentPlayed() >= 1.0 ) && ( extruderStartSeconds > 0.0)) { - buf[0] = '\0'; - float currentSeconds = Motherboard::getBoard().getCurrentSeconds() - extruderStartSeconds; - secs = ((currentSeconds / sdcard::getPercentPlayed()) * 100.0 ) - currentSeconds; - - if ((secs > 0.0 ) && (secs < 60.0) && ( ! buildComplete ) ) - lcd.writeFromPgmspace(time_left_1min); - else if (( secs <= 0.0) || ( host::isBuildComplete() ) || ( buildComplete ) ) { - buildComplete = true; - lcd.writeFromPgmspace(time_left_none); - } else { - appendTime(buf, sizeof(buf), (uint32_t)secs); - lcd.writeString(buf); - } - } - else lcd.writeFromPgmspace(time_left_calc); - - //Set extruderStartSeconds to when the extruder starts extruding, so we can - //get an accurate TimeLeft: - if ( extruderStartSeconds == 0.0 ) { - if (extruderControl(SLAVE_CMD_GET_MOTOR_1_PWM, EXTDR_CMD_GET, responsePacket, 0)) { - uint8_t pwm = responsePacket.read8(1); - if ( pwm ) extruderStartSeconds = Motherboard::getBoard().getCurrentSeconds(); - } + tsecs = buildDuration - command::estimateSeconds(); + + buf[0] = '\0'; + if ((tsecs > 0 ) && (tsecs < 60) && ( ! buildComplete ) ) { + appendUint8(buf, sizeof(buf), (uint8_t)tsecs); + lcd.writeString(buf); + lcd.writeFromPgmspace(time_left_secs); + } else if (( tsecs <= 0) || ( host::isBuildComplete() ) || ( buildComplete ) ) { + buildComplete = true; + lcd.writeFromPgmspace(time_left_none); + } else { + appendTime(buf, sizeof(buf), (uint32_t)tsecs); + lcd.writeString(buf); } break; - case 3: + case 3: // Zpos lcd.setCursor(0,1); lcd.writeFromPgmspace(zpos); lcd.setCursor(6,1); - Point position = steppers::getPosition(); + position = steppers::getPosition(); //Divide by the axis steps to mm's lcd.writeFloat(stepsToMM(position[2], AXIS_Z), 3); lcd.writeFromPgmspace(zpos_mm); break; + case 4: // Filament + lcd.setCursor(0,1); + lcd.writeFromPgmspace(filament); + lcd.setCursor(9,1); + //Get filament used and convert to meters + filamentUsed = stepsToMM(command::getFilamentLength(), AXIS_A) / 10000.0; + if ( filamentUsed < 0.01 ) { + filamentUsed *= 10000.0; //Back to mm's + precision = 1; + } + else if ( filamentUsed < 10.0 ) precision = 4; + else if ( filamentUsed < 100.0 ) precision = 3; + else precision = 2; + lcd.writeFloat(filamentUsed, precision); + if ( precision == 1 ) lcd.write('m'); + lcd.write('m'); + break; } - break; } updatePhase++; - if (updatePhase > 4) { + if (updatePhase > 5) { updatePhase = 0; } } @@ -1302,12 +1372,22 @@ void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { switch(host::getHostState()) { case host::HOST_STATE_BUILDING: case host::HOST_STATE_BUILDING_FROM_SD: + case host::HOST_STATE_ESTIMATING_FROM_SD: interface::pushScreen(&cancelBuildMenu); break; default: interface::popScreen(); break; } + case ButtonArray::OK: + if (( estimatingBuild ) && ( host::getHostState() == host::HOST_STATE_ESTIMATING_FROM_SD )) { + buildDuration = 0; + host::setHostStateBuildingFromSD(); + command::setEstimation(false); + overrideForceRedraw = true; + estimatingBuild = false; + } + break; } } @@ -1460,13 +1540,15 @@ CancelBuildMenu::CancelBuildMenu() { itemCount = 5; reset(); pauseDisabled = false; - if (( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; + if ( ( estimatingBuild ) || ( steppers::isHoming() ) || + (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; } void CancelBuildMenu::resetState() { pauseMode.autoPause = false; pauseDisabled = false; - if (( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; + if ( ( estimatingBuild ) || ( steppers::isHoming() ) || + (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; if ( pauseDisabled ) { itemIndex = 2; @@ -1486,7 +1568,8 @@ void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar pause[] = "Pause "; const static PROGMEM prog_uchar back[] = "Continue Build"; - if (( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; + if ( ( estimatingBuild ) || ( steppers::isHoming() ) || + (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; //Implement variable length menu uint8_t lind = 0; @@ -1879,8 +1962,10 @@ void SDMenu::handleSelect(uint8_t index) { return; } + estimatingBuild = true; + command::setEstimation(true); sdcard::SdErrorCode e; - e = host::startBuildFromSD(); + e = host::startBuildFromSD(true); if (e != sdcard::SD_SUCCESS) { // TODO: report error return; diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 3a9cd54..0355a20 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -260,11 +260,12 @@ private: uint8_t updatePhase; uint8_t buildTimePhase; float lastElapsedSeconds; - float extruderStartSeconds; bool buildComplete; //For solving floating point rounding issues PauseMode pauseMode; bool pausePushLockout; bool buildCompleteBuzzPlayed; + int32_t buildDuration; + bool overrideForceRedraw; public: micros_t getUpdateRate() {return 500L * 1000L;} From 4e297c5daf5483fdcd596380d6f12044ce261e9c Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Sat, 21 Jan 2012 19:30:09 -0700 Subject: [PATCH 35/61] Added "Filament Used" menu to indicate the lifetime amount of filament used. Also fixed display bug in "Filament Used" in build menu. --- firmware/src/Motherboard/Command.cc | 39 +++++++- firmware/src/Motherboard/Command.hh | 3 + firmware/src/Motherboard/EepromMap.cc | 1 + firmware/src/Motherboard/EepromMap.hh | 3 + firmware/src/Motherboard/Host.cc | 1 + firmware/src/shared/Menu.cc | 131 +++++++++++++++++++++++--- firmware/src/shared/Menu.hh | 26 +++++ 7 files changed, 190 insertions(+), 14 deletions(-) diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index 7baf488..bfc3fd6 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -25,6 +25,7 @@ #include #include #include "EepromMap.hh" +#include "Eeprom.hh" #include "SDCard.hh" #include "ExtruderControl.hh" @@ -45,7 +46,7 @@ volatile int32_t pauseZPos = 0; bool estimating = false; int64_t estimateTimeUs = 0; -int64_t filamentLength = 0; //This maybe pos or neg, but ABS it and all is good (in steps) +volatile int64_t filamentLength = 0; //This maybe pos or neg, but ABS it and all is good (in steps) Point lastPosition; @@ -136,6 +137,25 @@ void reset() { } +//Adds the filament used during this build + +void addFilamentUsed() { + //Need to do this to get the absolute amount + int64_t fl = getFilamentLength(); + + if ( fl > 0 ) { + cli(); + int64_t filamentUsed = eeprom::getEepromInt64(eeprom::FILAMENT_USED, 0); + filamentUsed += fl; + eeprom::putEepromInt64(eeprom::FILAMENT_USED, filamentUsed); + sei(); + + //We've used it up, so reset it + filamentLength = 0; + } +} + + //Executes a slave command //Returns true if everything is okay, otherwise false in case of error //The result from the command is stored in result @@ -519,11 +539,24 @@ void runCommandSlice() { out.reset(); command_buffer.pop(); // remove the command code out.append8(command_buffer.pop()); // copy tool index - out.append8(command_buffer.pop()); // copy command code + uint8_t commandCode = command_buffer.pop(); + out.append8(commandCode); // copy command code + int len = pop8(); // get payload length + + uint8_t buf[4]; for (int i = 0; i < len; i++) { - out.append8(command_buffer.pop()); + uint8_t b = command_buffer.pop(); + if ( i < 4 ) buf[i] = b; + out.append8(b); } + + if (( commandCode == SLAVE_CMD_SET_TEMP ) && ( ! estimating ) && + ( ! sdcard::isPlaying()) ) { + uint16_t *temp = (uint16_t *)&buf[0]; + if ( *temp == 0 ) addFilamentUsed(); + } + // we don't care about the response, so we can release // the lock after we initiate the transfer tool::startTransaction(); diff --git a/firmware/src/Motherboard/Command.hh b/firmware/src/Motherboard/Command.hh index 1a761b4..eec7a70 100644 --- a/firmware/src/Motherboard/Command.hh +++ b/firmware/src/Motherboard/Command.hh @@ -28,6 +28,9 @@ namespace command { /// commands. void reset(); +/// Adds the filament used in this build to eeprom +void addFilamentUsed(); + /// Run the command thread slice. void runCommandSlice(); diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index 568feb0..bd0dee4 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -46,6 +46,7 @@ void setDefaults() { putEepromInt64(eeprom::STEPS_PER_MM_Z,STEPS_PER_MM_Z_DEFAULT); putEepromInt64(eeprom::STEPS_PER_MM_A,STEPS_PER_MM_A_DEFAULT); putEepromInt64(eeprom::STEPS_PER_MM_B,STEPS_PER_MM_B_DEFAULT); + putEepromInt64(eeprom::FILAMENT_USED,0); } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index 94c60aa..20736e1 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -82,6 +82,9 @@ const static uint16_t STEPS_PER_MM_Z = 0x009B; const static uint16_t STEPS_PER_MM_A = 0x00A3; const static uint16_t STEPS_PER_MM_B = 0x00AB; +//int64_t (bytes) The filament used in steps +const static uint16_t FILAMENT_USED = 0x00B3; + /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/Motherboard/Host.cc b/firmware/src/Motherboard/Host.cc index 6e7e450..279f81c 100644 --- a/firmware/src/Motherboard/Host.cc +++ b/firmware/src/Motherboard/Host.cc @@ -466,6 +466,7 @@ bool processQueryPacket(const InPacket& from_host, OutPacket& to_host) { case HOST_CMD_ABORT: // equivalent at current time case HOST_CMD_RESET: // TODO: This is fishy. + command::addFilamentUsed(); if (currentState == HOST_STATE_BUILDING || currentState == HOST_STATE_BUILDING_FROM_SD || currentState == HOST_STATE_ESTIMATING_FROM_SD) { diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index a00a4df..8d8342f 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -37,6 +37,8 @@ bool estimatingBuild = false; Point pausedPosition, homePosition; +float holdFilamentUsed; + //Stored using STEPS_PER_MM_PRECISION int64_t axisStepsPerMM[5]; @@ -1101,6 +1103,7 @@ void MonitorMode::reset() { pauseMode.autoPause = false; buildCompleteBuzzPlayed = false; overrideForceRedraw = false; + holdFilamentUsed = 0.0; } @@ -1320,6 +1323,11 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeString(buf); lcd.writeFromPgmspace(time_left_secs); } else if (( tsecs <= 0) || ( host::isBuildComplete() ) || ( buildComplete ) ) { + if ( ! buildComplete ) { + //Store it so we can continue to use it + holdFilamentUsed = stepsToMM(command::getFilamentLength(), AXIS_A); + command::addFilamentUsed(); + } buildComplete = true; lcd.writeFromPgmspace(time_left_none); } else { @@ -1343,10 +1351,11 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,1); lcd.writeFromPgmspace(filament); lcd.setCursor(9,1); - //Get filament used and convert to meters - filamentUsed = stepsToMM(command::getFilamentLength(), AXIS_A) / 10000.0; - if ( filamentUsed < 0.01 ) { - filamentUsed *= 10000.0; //Back to mm's + if ( holdFilamentUsed != 0.0 ) filamentUsed = holdFilamentUsed; + else filamentUsed = stepsToMM(command::getFilamentLength(), AXIS_A); + filamentUsed /= 1000.0; //convert to meters + if ( filamentUsed < 0.1 ) { + filamentUsed *= 1000.0; //Back to mm's precision = 1; } else if ( filamentUsed < 10.0 ) precision = 4; @@ -1607,6 +1616,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { lind ++; if ( index == lind) { + command::addFilamentUsed(); // Cancel build, returning to whatever menu came before monitor mode. // TODO: Cancel build. interface::popScreen(); @@ -1649,7 +1659,7 @@ int64_t MainMenu::checkAndGetEepromDefault(const uint16_t location, const int64_ } MainMenu::MainMenu() { - itemCount = 17; + itemCount = 18; reset(); //Read in the axisStepsPerMM, we'll need these for various firmware functions later on @@ -1676,6 +1686,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar extruderFan[] = "Extruder Fan"; const static PROGMEM prog_uchar calibrate[] = "Calibrate"; const static PROGMEM prog_uchar homeOffsets[] = "Home Offsets"; + const static PROGMEM prog_uchar filamentUsed[] = "Filament Used"; const static PROGMEM prog_uchar endStops[] = "Test End Stops"; const static PROGMEM prog_uchar stepsPerMm[] = "Axis Steps:mm"; const static PROGMEM prog_uchar versions[] = "Version"; @@ -1722,15 +1733,18 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(homeOffsets); break; case 13: - lcd.writeFromPgmspace(endStops); + lcd.writeFromPgmspace(filamentUsed); break; case 14: - lcd.writeFromPgmspace(stepsPerMm); + lcd.writeFromPgmspace(endStops); break; case 15: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(stepsPerMm); break; case 16: + lcd.writeFromPgmspace(versions); + break; + case 17: lcd.writeFromPgmspace(snake); break; } @@ -1792,18 +1806,22 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&homeOffsetsMode); break; case 13: + // Show Filament Used Mode + interface::pushScreen(&filamentUsedMode); + break; + case 14: // Show test end stops menu interface::pushScreen(&testEndStopsMode); break; - case 14: + case 15: // Show steps per mm menu interface::pushScreen(&stepsPerMMMode); break; - case 15: + case 16: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 16: + case 17: // Show build from SD screen interface::pushScreen(&snake); break; @@ -3236,4 +3254,95 @@ void StepsPerMMMode::notifyButtonPressed(ButtonArray::ButtonName button) { axisStepsPerMM[currentIndex] = spm; } +FilamentUsedResetMenu::FilamentUsedResetMenu() { + itemCount = 4; + reset(); +} + +void FilamentUsedResetMenu::resetState() { + itemIndex = 2; + firstItemIndex = 2; +} + +void FilamentUsedResetMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg[] = "Reset To Zero?"; + const static PROGMEM prog_uchar no[] = "No"; + const static PROGMEM prog_uchar yes[]= "Yes"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg); + break; + case 1: + break; + case 2: + lcd.writeFromPgmspace(no); + break; + case 3: + lcd.writeFromPgmspace(yes); + break; + } +} + +void FilamentUsedResetMenu::handleSelect(uint8_t index) { + switch (index) { + case 3: + //Reset to zero + eeprom::putEepromInt64(eeprom::FILAMENT_USED, 0); + case 2: + interface::popScreen(); + interface::popScreen(); + break; + } +} + +void FilamentUsedMode::reset() { +} + +void FilamentUsedMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Filament Used:"; + const static PROGMEM prog_uchar message3[] = " (reset)"; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + int64_t filamentUsed = eeprom::getEepromInt64(eeprom::FILAMENT_USED, 0); + float filamentUsedMM = stepsToMM(filamentUsed, AXIS_A); + + lcd.setCursor(0,1); + lcd.writeFloat(filamentUsedMM / 1000.0, 4); + lcd.write('m'); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(message3); + + lcd.setCursor(0,3); + lcd.writeFloat(((filamentUsedMM / 25.4) / 12.0), 4); + lcd.writeString("ft"); + } +} + +void FilamentUsedMode::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + interface::pushScreen(&filamentUsedResetMenu); + break; + case ButtonArray::ZPLUS: + case ButtonArray::ZMINUS: + case ButtonArray::YPLUS: + case ButtonArray::YMINUS: + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 0355a20..c02d94e 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -590,6 +590,31 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class FilamentUsedResetMenu: public Menu { +public: + FilamentUsedResetMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + +class FilamentUsedMode: public Screen { +private: + FilamentUsedResetMenu filamentUsedResetMenu; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -614,6 +639,7 @@ private: CalibrateMode calibrateMode; HomeOffsetsMode homeOffsetsMode; StepsPerMMMode stepsPerMMMode; + FilamentUsedMode filamentUsedMode; TestEndStopsMode testEndStopsMode; VersionMode versionMode; MoodLightMode moodLightMode; From b131898f0ee6dbaab8c0192ef2d30a154ab8ecb3 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Mon, 23 Jan 2012 14:48:11 -0700 Subject: [PATCH 36/61] Added ABP Printing Multiple Copies. This is accessible via the new "Build Settings" menu. Also, new build phase information added for the copy number. "Print another" added to the Cancel Build Menu after print has completely finished. --- firmware/src/Motherboard/Command.cc | 23 ++- firmware/src/Motherboard/Command.hh | 7 + firmware/src/Motherboard/EepromMap.cc | 1 + firmware/src/Motherboard/EepromMap.hh | 5 +- firmware/src/shared/Menu.cc | 253 +++++++++++++++++++++----- firmware/src/shared/Menu.hh | 33 +++- 6 files changed, 277 insertions(+), 45 deletions(-) diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index bfc3fd6..cd230b5 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -46,7 +46,8 @@ volatile int32_t pauseZPos = 0; bool estimating = false; int64_t estimateTimeUs = 0; -volatile int64_t filamentLength = 0; //This maybe pos or neg, but ABS it and all is good (in steps) +volatile int64_t filamentLength = 0; //This maybe pos or neg, but ABS it and all is good (in steps) +volatile int64_t lastFilamentLength = 0; Point lastPosition; @@ -133,6 +134,7 @@ void reset() { command_buffer.reset(); estimateTimeUs = 0; filamentLength = 0; + lastFilamentLength = 0; mode = READY; } @@ -151,6 +153,7 @@ void addFilamentUsed() { sei(); //We've used it up, so reset it + lastFilamentLength = filamentLength; filamentLength = 0; } } @@ -212,6 +215,11 @@ int64_t getFilamentLength() { return filamentLength; } +int64_t getLastFilamentLength() { + if ( lastFilamentLength < 0 ) return -lastFilamentLength; + return lastFilamentLength; +} + int32_t estimateSeconds() { return estimateTimeUs / 1000000; } @@ -232,6 +240,19 @@ void setEstimation(bool on) { estimating = on; } +void buildAnotherCopy() { + if ( estimating ) setEstimation(false); + + recentCommandClock = 0; + recentCommandTime = 0; + command_buffer.reset(); + sdcard::playbackRestart(); + estimateTimeUs = 0; + + addFilamentUsed(); + lastFilamentLength = 0; +} + void estimateDelay(uint32_t microseconds) { estimateTimeUs += (int64_t)microseconds; } diff --git a/firmware/src/Motherboard/Command.hh b/firmware/src/Motherboard/Command.hh index eec7a70..deb01bd 100644 --- a/firmware/src/Motherboard/Command.hh +++ b/firmware/src/Motherboard/Command.hh @@ -55,12 +55,19 @@ int32_t getPauseAtZPos(); /// Returns the length of filament extruded (in steps) int64_t getFilamentLength(); +/// Returns the length of filament extruded (in steps) prior to the +/// last time the filament was added to the filament count +int64_t getLastFilamentLength(); + //Returns the number of seconds estimated int32_t estimateSeconds(); //Set the estimation mode void setEstimation(bool on); +//Build another copy +void buildAnotherCopy(); + /// Check the remaining capacity of the command buffer /// \return Amount of space left in the buffer, in bytes uint16_t getRemainingCapacity(); diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index bd0dee4..862cf06 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -47,6 +47,7 @@ void setDefaults() { putEepromInt64(eeprom::STEPS_PER_MM_A,STEPS_PER_MM_A_DEFAULT); putEepromInt64(eeprom::STEPS_PER_MM_B,STEPS_PER_MM_B_DEFAULT); putEepromInt64(eeprom::FILAMENT_USED,0); + eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,1); } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index 20736e1..077ff92 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -82,9 +82,12 @@ const static uint16_t STEPS_PER_MM_Z = 0x009B; const static uint16_t STEPS_PER_MM_A = 0x00A3; const static uint16_t STEPS_PER_MM_B = 0x00AB; -//int64_t (bytes) The filament used in steps +//int64_t (8 bytes) The filament used in steps const static uint16_t FILAMENT_USED = 0x00B3; +//Number of ABP copies (1-254) when building from SDCard (1 byte) +const static uint16_t ABP_COPIES = 0x00C3; + /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 8d8342f..126b515 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -37,8 +37,6 @@ bool estimatingBuild = false; Point pausedPosition, homePosition; -float holdFilamentUsed; - //Stored using STEPS_PER_MM_PRECISION int64_t axisStepsPerMM[5]; @@ -1097,13 +1095,13 @@ void SnakeMode::notifyButtonPressed(ButtonArray::ButtonName button) { void MonitorMode::reset() { updatePhase = 0; buildTimePhase = 0; - buildComplete = false; lastElapsedSeconds = 0.0; pausePushLockout = false; pauseMode.autoPause = false; buildCompleteBuzzPlayed = false; overrideForceRedraw = false; - holdFilamentUsed = 0.0; + copiesPrinted = 0; + timeLeftDisplayed = false; } @@ -1120,8 +1118,9 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar zpos_mm[] = "mm"; const static PROGMEM prog_uchar estimate2[] = "Estimating: 0%"; const static PROGMEM prog_uchar estimate3[] = " (skip)"; - const static PROGMEM prog_uchar estimate4[] = "Duration: 0h00m"; const static PROGMEM prog_uchar filament[] = "Filament:0.00m "; + const static PROGMEM prog_uchar copies[] = "Copy: "; + const static PROGMEM prog_uchar of[] = " of "; char buf[17]; if ( command::isPaused() ) { @@ -1136,6 +1135,19 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( host::getHostState() != host::HOST_STATE_ESTIMATING_FROM_SD ) estimatingBuild = false; + //Check for a build complete, and if we have more than one copy + //to print, setup another one + if (( ! estimatingBuild ) && ( host::isBuildComplete() )) { + uint8_t copiesToPrint = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); + if ( copiesToPrint > 1 ) { + if ( copiesPrinted < (copiesToPrint - 1)) { + copiesPrinted ++; + overrideForceRedraw = true; + command::buildAnotherCopy(); + } + } + } + if ((forceRedraw) || (overrideForceRedraw)) { lcd.clear(); lcd.setCursor(0,0); @@ -1153,7 +1165,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,2); lcd.writeFromPgmspace(estimate3); lcd.setCursor(0,3); - lcd.writeFromPgmspace(estimate4); + lcd.writeFromPgmspace(duration); } else { lcd.writeFromPgmspace(completed_percent); } @@ -1194,7 +1206,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeString(buf); //Check for estimate finished, and switch states to building - if (( ! sdcard::playbackHasNext() ) && ( command::isEmpty())) { + if ( host::isBuildComplete() ) { //Store the estimate seconds buildDuration = command::estimateSeconds(); host::setHostStateBuildingFromSD(); @@ -1272,14 +1284,22 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if (( buildDuration == 0 ) && ( buildTimePhase == 2 )) buildTimePhase ++; } - if ( buildTimePhase >= 5 ) buildTimePhase = 0; + //If we're setup to print more than one copy, then show that build phase, + //otherwise skip it + uint8_t totalCopies; + if ( buildTimePhase == 5 ) { + totalCopies = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); + if ( totalCopies <= 1 ) buildTimePhase ++; + } + + if ( buildTimePhase >= 6 ) buildTimePhase = 0; float secs; int32_t tsecs; Point position; uint8_t precision; - float filamentUsed; float completedPercent; + float filamentUsed, lastFilamentUsed; switch (buildTimePhase) { case 0: //Completed Percent @@ -1311,24 +1331,22 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { break; case 2: // Time Left lcd.setCursor(0,1); - if ( command::getFilamentLength() >= 1 ) lcd.writeFromPgmspace(time_left); + if (( timeLeftDisplayed ) || ( command::getFilamentLength() >= 1 )) { + lcd.writeFromPgmspace(time_left); + timeLeftDisplayed = true; + } else lcd.writeFromPgmspace(duration); lcd.setCursor(9,1); tsecs = buildDuration - command::estimateSeconds(); buf[0] = '\0'; - if ((tsecs > 0 ) && (tsecs < 60) && ( ! buildComplete ) ) { + if ((tsecs > 0 ) && (tsecs < 60) && ( host::isBuildComplete() ) ) { appendUint8(buf, sizeof(buf), (uint8_t)tsecs); lcd.writeString(buf); lcd.writeFromPgmspace(time_left_secs); - } else if (( tsecs <= 0) || ( host::isBuildComplete() ) || ( buildComplete ) ) { - if ( ! buildComplete ) { - //Store it so we can continue to use it - holdFilamentUsed = stepsToMM(command::getFilamentLength(), AXIS_A); - command::addFilamentUsed(); - } - buildComplete = true; + } else if (( tsecs <= 0) || ( host::isBuildComplete()) ) { + command::addFilamentUsed(); lcd.writeFromPgmspace(time_left_none); } else { appendTime(buf, sizeof(buf), (uint32_t)tsecs); @@ -1351,7 +1369,8 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,1); lcd.writeFromPgmspace(filament); lcd.setCursor(9,1); - if ( holdFilamentUsed != 0.0 ) filamentUsed = holdFilamentUsed; + lastFilamentUsed = stepsToMM(command::getLastFilamentLength(), AXIS_A); + if ( lastFilamentUsed != 0.0 ) filamentUsed = lastFilamentUsed; else filamentUsed = stepsToMM(command::getFilamentLength(), AXIS_A); filamentUsed /= 1000.0; //convert to meters if ( filamentUsed < 0.1 ) { @@ -1365,12 +1384,20 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( precision == 1 ) lcd.write('m'); lcd.write('m'); break; + case 5: // Copies printed + lcd.setCursor(0,1); + lcd.writeFromPgmspace(copies); + lcd.setCursor(7,1); + lcd.writeFloat((float)(copiesPrinted + 1), 0); + lcd.writeFromPgmspace(of); + lcd.writeFloat((float)totalCopies, 0); + break; } break; } updatePhase++; - if (updatePhase > 5) { + if (updatePhase > 6) { updatePhase = 0; } } @@ -1551,6 +1578,11 @@ CancelBuildMenu::CancelBuildMenu() { pauseDisabled = false; if ( ( estimatingBuild ) || ( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; + + if (( ! estimatingBuild ) && ( host::isBuildComplete() )) + printAnotherEnabled = true; + else printAnotherEnabled = false; + } void CancelBuildMenu::resetState() { @@ -1559,6 +1591,10 @@ void CancelBuildMenu::resetState() { if ( ( estimatingBuild ) || ( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; + if (( ! estimatingBuild ) && ( host::isBuildComplete() )) + printAnotherEnabled = true; + else printAnotherEnabled = false; + if ( pauseDisabled ) { itemIndex = 2; itemCount = 4; @@ -1567,15 +1603,20 @@ void CancelBuildMenu::resetState() { itemCount = 5; } + if ( printAnotherEnabled ) { + itemIndex = 1; + } + firstItemIndex = itemIndex; } void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar choose[] = "Please Choose:"; - const static PROGMEM prog_uchar abort[] = "Abort Print "; - const static PROGMEM prog_uchar pauseZ[] = "Pause at ZPos "; - const static PROGMEM prog_uchar pause[] = "Pause "; - const static PROGMEM prog_uchar back[] = "Continue Build"; + const static PROGMEM prog_uchar choose[] = "Please Choose:"; + const static PROGMEM prog_uchar abort[] = "Abort Print "; + const static PROGMEM prog_uchar printAnother[] = "Print Another"; + const static PROGMEM prog_uchar pauseZ[] = "Pause at ZPos "; + const static PROGMEM prog_uchar pause[] = "Pause "; + const static PROGMEM prog_uchar back[] = "Continue Build"; if ( ( estimatingBuild ) || ( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; @@ -1586,11 +1627,16 @@ void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { if ( index == lind ) lcd.writeFromPgmspace(choose); lind ++; - if ( pauseDisabled ) lind ++; + if (( pauseDisabled ) && ( ! printAnotherEnabled )) lind ++; if ( index == lind) lcd.writeFromPgmspace(abort); lind ++; + if ( printAnotherEnabled ) { + if ( index == lind ) lcd.writeFromPgmspace(printAnother); + lind ++; + } + if ( ! pauseDisabled ) { if ( index == lind ) lcd.writeFromPgmspace(pauseZ); lind ++; @@ -1611,7 +1657,7 @@ void CancelBuildMenu::handleSelect(uint8_t index) { //Implement variable length menu uint8_t lind = 0; - if ( pauseDisabled ) lind ++; + if (( pauseDisabled ) && ( ! printAnotherEnabled )) lind ++; lind ++; @@ -1624,6 +1670,14 @@ void CancelBuildMenu::handleSelect(uint8_t index) { } lind ++; + if ( printAnotherEnabled ) { + if ( index == lind ) { + command::buildAnotherCopy(); + interface::popScreen(); + } + lind ++; + } + if ( ! pauseDisabled ) { if ( index == lind ) interface::pushScreen(&pauseAtZPosScreen); lind ++; @@ -1659,7 +1713,7 @@ int64_t MainMenu::checkAndGetEepromDefault(const uint16_t location, const int64_ } MainMenu::MainMenu() { - itemCount = 18; + itemCount = 19; reset(); //Read in the axisStepsPerMM, we'll need these for various firmware functions later on @@ -1683,6 +1737,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar steppersS[] = "Steppers"; const static PROGMEM prog_uchar moodlight[] = "Mood Light"; const static PROGMEM prog_uchar buzzer[] = "Buzzer"; + const static PROGMEM prog_uchar buildSettings[] = "Build Settings"; const static PROGMEM prog_uchar extruderFan[] = "Extruder Fan"; const static PROGMEM prog_uchar calibrate[] = "Calibrate"; const static PROGMEM prog_uchar homeOffsets[] = "Home Offsets"; @@ -1724,27 +1779,30 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(buzzer); break; case 10: - lcd.writeFromPgmspace(extruderFan); + lcd.writeFromPgmspace(buildSettings); break; case 11: - lcd.writeFromPgmspace(calibrate); + lcd.writeFromPgmspace(extruderFan); break; case 12: - lcd.writeFromPgmspace(homeOffsets); + lcd.writeFromPgmspace(calibrate); break; case 13: - lcd.writeFromPgmspace(filamentUsed); + lcd.writeFromPgmspace(homeOffsets); break; case 14: - lcd.writeFromPgmspace(endStops); + lcd.writeFromPgmspace(filamentUsed); break; case 15: - lcd.writeFromPgmspace(stepsPerMm); + lcd.writeFromPgmspace(endStops); break; case 16: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(stepsPerMm); break; case 17: + lcd.writeFromPgmspace(versions); + break; + case 18: lcd.writeFromPgmspace(snake); break; } @@ -1794,34 +1852,38 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&buzzerSetRepeats); break; case 10: + // Show Build Settings Mode + interface::pushScreen(&buildSettingsMenu); + break; + case 11: // Show Extruder Fan Mode interface::pushScreen(&extruderFanMenu); break; - case 11: + case 12: // Show Calibrate Mode interface::pushScreen(&calibrateMode); break; - case 12: + case 13: // Show Home Offsets Mode interface::pushScreen(&homeOffsetsMode); break; - case 13: + case 14: // Show Filament Used Mode interface::pushScreen(&filamentUsedMode); break; - case 14: + case 15: // Show test end stops menu interface::pushScreen(&testEndStopsMode); break; - case 15: + case 16: // Show steps per mm menu interface::pushScreen(&stepsPerMMMode); break; - case 16: + case 17: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 17: + case 18: // Show build from SD screen interface::pushScreen(&snake); break; @@ -3345,4 +3407,111 @@ void FilamentUsedMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } +BuildSettingsMenu::BuildSettingsMenu() { + itemCount = 1; + reset(); +} + +void BuildSettingsMenu::resetState() { + itemIndex = 0; + firstItemIndex = 0; +} + +void BuildSettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar item1[] = "ABP Copies (SD)"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(item1); + break; + case 1: + break; + case 2: + break; + case 3: + break; + } +} + +void BuildSettingsMenu::handleSelect(uint8_t index) { + OutPacket responsePacket; + + switch (index) { + case 0: + //Change number of ABP copies + interface::pushScreen(&abpCopiesSetScreen); + break; + } +} + +void ABPCopiesSetScreen::reset() { + value = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); + if ( value < 1 ) { + eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,1); + value = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); //Just in case + } +} + +void ABPCopiesSetScreen::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "ABP Copies (SD):"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(0,1); + lcd.writeInt(value,3); +} + +void ABPCopiesSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,value); + interface::popScreen(); + interface::popScreen(); + break; + case ButtonArray::ZPLUS: + // increment more + if (value <= 249) { + value += 5; + } + break; + case ButtonArray::ZMINUS: + // decrement more + if (value >= 6) { + value -= 5; + } + break; + case ButtonArray::YPLUS: + // increment less + if (value <= 253) { + value += 1; + } + break; + case ButtonArray::YMINUS: + // decrement less + if (value >= 2) { + value -= 1; + } + break; + + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index c02d94e..87c9d47 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -250,6 +250,7 @@ private: PauseMode pauseMode; bool pauseDisabled; PauseAtZPosScreen pauseAtZPosScreen; + bool printAnotherEnabled; }; @@ -260,12 +261,13 @@ private: uint8_t updatePhase; uint8_t buildTimePhase; float lastElapsedSeconds; - bool buildComplete; //For solving floating point rounding issues PauseMode pauseMode; bool pausePushLockout; bool buildCompleteBuzzPlayed; int32_t buildDuration; bool overrideForceRedraw; + uint8_t copiesPrinted; + bool timeLeftDisplayed; public: micros_t getUpdateRate() {return 500L * 1000L;} @@ -615,6 +617,34 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class ABPCopiesSetScreen: public Screen { +private: + uint8_t value; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + + +class BuildSettingsMenu: public Menu { +private: + ABPCopiesSetScreen abpCopiesSetScreen; +public: + BuildSettingsMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -635,6 +665,7 @@ private: SteppersMenu steppersMenu; AdvanceABPMode advanceABPMode; BuzzerSetRepeatsMode buzzerSetRepeats; + BuildSettingsMenu buildSettingsMenu; ExtruderFanMenu extruderFanMenu; CalibrateMode calibrateMode; HomeOffsetsMode homeOffsetsMode; From df49082841f663d65716bf5934f9b95b55fbe1fc Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Mon, 23 Jan 2012 16:02:36 -0700 Subject: [PATCH 37/61] Added "Override GCode Build Temperature" and "Preheat During Estimate" options to "Build Settings" menu. --- firmware/src/Motherboard/Command.cc | 39 +++++++-- firmware/src/Motherboard/EepromMap.cc | 2 + firmware/src/Motherboard/EepromMap.hh | 7 ++ firmware/src/shared/Menu.cc | 119 +++++++++++++++++++++++++- firmware/src/shared/Menu.hh | 25 +++++- 5 files changed, 181 insertions(+), 11 deletions(-) diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index cd230b5..2e1e6b4 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -48,6 +48,8 @@ bool estimating = false; int64_t estimateTimeUs = 0; volatile int64_t filamentLength = 0; //This maybe pos or neg, but ABS it and all is good (in steps) volatile int64_t lastFilamentLength = 0; +volatile bool firstHeatTool0; +volatile bool firstHeatHbp; Point lastPosition; @@ -135,6 +137,8 @@ void reset() { estimateTimeUs = 0; filamentLength = 0; lastFilamentLength = 0; + firstHeatTool0 = true; + firstHeatHbp = true; mode = READY; } @@ -248,6 +252,8 @@ void buildAnotherCopy() { command_buffer.reset(); sdcard::playbackRestart(); estimateTimeUs = 0; + firstHeatTool0 = true; + firstHeatHbp = true; addFilamentUsed(); lastFilamentLength = 0; @@ -566,18 +572,39 @@ void runCommandSlice() { int len = pop8(); // get payload length uint8_t buf[4]; - for (int i = 0; i < len; i++) { - uint8_t b = command_buffer.pop(); - if ( i < 4 ) buf[i] = b; - out.append8(b); - } - + for (int i = 0; (i < len) && ( i < 4); i ++) + buf[i] = command_buffer.pop(); + if (( commandCode == SLAVE_CMD_SET_TEMP ) && ( ! estimating ) && ( ! sdcard::isPlaying()) ) { uint16_t *temp = (uint16_t *)&buf[0]; if ( *temp == 0 ) addFilamentUsed(); } + uint8_t overrideTemp = 0; + if ( commandCode == SLAVE_CMD_SET_TEMP ) { + uint16_t *temp = (uint16_t *)&buf[0]; + if (( *temp != 0 ) && ( firstHeatTool0 ) && ( eeprom::getEeprom8(eeprom::OVERRIDE_GCODE_TEMP, 0) )) { + firstHeatTool0 = false; + overrideTemp = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220); + *temp = overrideTemp; + } + } + if ( commandCode == SLAVE_CMD_SET_PLATFORM_TEMP ) { + uint16_t *temp = (uint16_t *)&buf[0]; + if (( *temp != 0 ) && ( firstHeatHbp ) && ( eeprom::getEeprom8(eeprom::OVERRIDE_GCODE_TEMP, 0) )) { + firstHeatHbp = false; + overrideTemp = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110); + *temp = overrideTemp; + } + } + + for (int i = 0; (i < len) && ( i < 4); i ++) + out.append8(buf[i]); + + for (int i = 4; i < len; i ++ ) + out.append8(command_buffer.pop()); + // we don't care about the response, so we can release // the lock after we initiate the transfer tool::startTransaction(); diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index 862cf06..dfd350d 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -48,6 +48,8 @@ void setDefaults() { putEepromInt64(eeprom::STEPS_PER_MM_B,STEPS_PER_MM_B_DEFAULT); putEepromInt64(eeprom::FILAMENT_USED,0); eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,1); + eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,0); + eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,0); } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index 077ff92..9b0e7ae 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -88,6 +88,13 @@ const static uint16_t FILAMENT_USED = 0x00B3; //Number of ABP copies (1-254) when building from SDCard (1 byte) const static uint16_t ABP_COPIES = 0x00C3; +//Preheat during estimate 0 = Disable, 1 = Enabled +const static uint16_t PREHEAT_DURING_ESTIMATE = 0x00C4; + +//Override the temperature set in the gcode file at the start of the build +//0 = Disable, 1 = Enabled +const static uint16_t OVERRIDE_GCODE_TEMP = 0x00C5; + /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 126b515..3414583 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1192,6 +1192,15 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { //Display estimating stats if ( estimatingBuild ) { + //If preheat_during_estimate is set, then preheat + OutPacket responsePacket; + if ( eeprom::getEeprom8(eeprom::PREHEAT_DURING_ESTIMATE, 0) ) { + uint8_t value = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220); + extruderControl(SLAVE_CMD_SET_TEMP, EXTDR_CMD_SET, responsePacket, (uint16_t)value); + value = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110); + extruderControl(SLAVE_CMD_SET_PLATFORM_TEMP, EXTDR_CMD_SET, responsePacket, value); + } + //Write out the % estimated lcd.setCursor(12,1); buf[0] = '\0'; @@ -3408,7 +3417,7 @@ void FilamentUsedMode::notifyButtonPressed(ButtonArray::ButtonName button) { } BuildSettingsMenu::BuildSettingsMenu() { - itemCount = 1; + itemCount = 3; reset(); } @@ -3418,17 +3427,19 @@ void BuildSettingsMenu::resetState() { } void BuildSettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar item1[] = "ABP Copies (SD)"; + const static PROGMEM prog_uchar item1[] = "EstimatePreheat"; + const static PROGMEM prog_uchar item2[] = "Override Temp"; + const static PROGMEM prog_uchar item3[] = "ABP Copies (SD)"; switch (index) { case 0: lcd.writeFromPgmspace(item1); break; case 1: + lcd.writeFromPgmspace(item2); break; case 2: - break; - case 3: + lcd.writeFromPgmspace(item3); break; } } @@ -3438,6 +3449,14 @@ void BuildSettingsMenu::handleSelect(uint8_t index) { switch (index) { case 0: + //Preheat during estimation + interface::pushScreen(&preheatDuringEstimateMenu); + break; + case 1: + //Override the gcode temperature + interface::pushScreen(&overrideGCodeTempMenu); + break; + case 2: //Change number of ABP copies interface::pushScreen(&abpCopiesSetScreen); break; @@ -3514,4 +3533,96 @@ void ABPCopiesSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) { } } +PreheatDuringEstimateMenu::PreheatDuringEstimateMenu() { + itemCount = 4; + reset(); +} + +void PreheatDuringEstimateMenu::resetState() { + if ( eeprom::getEeprom8(eeprom::PREHEAT_DURING_ESTIMATE, 0) ) itemIndex = 3; + else itemIndex = 2; + firstItemIndex = 2; +} + +void PreheatDuringEstimateMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg1[] = "Preheat during"; + const static PROGMEM prog_uchar msg2[] = "estimate phase:"; + const static PROGMEM prog_uchar disable[] = "Disable"; + const static PROGMEM prog_uchar enable[] = "Enable"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg1); + break; + case 1: + lcd.writeFromPgmspace(msg2); + break; + case 2: + lcd.writeFromPgmspace(disable); + break; + case 3: + lcd.writeFromPgmspace(enable); + break; + } +} + +void PreheatDuringEstimateMenu::handleSelect(uint8_t index) { + switch (index) { + case 2: + eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,0); + interface::popScreen(); + break; + case 3: + eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,1); + interface::popScreen(); + break; + } +} + +OverrideGCodeTempMenu::OverrideGCodeTempMenu() { + itemCount = 4; + reset(); +} + +void OverrideGCodeTempMenu::resetState() { + if ( eeprom::getEeprom8(eeprom::OVERRIDE_GCODE_TEMP, 0) ) itemIndex = 3; + else itemIndex = 2; + firstItemIndex = 2; +} + +void OverrideGCodeTempMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg1[] = "Override GCode"; + const static PROGMEM prog_uchar msg2[] = "Termperature:"; + const static PROGMEM prog_uchar disable[] = "Disable"; + const static PROGMEM prog_uchar enable[] = "Enable"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg1); + break; + case 1: + lcd.writeFromPgmspace(msg2); + break; + case 2: + lcd.writeFromPgmspace(disable); + break; + case 3: + lcd.writeFromPgmspace(enable); + break; + } +} + +void OverrideGCodeTempMenu::handleSelect(uint8_t index) { + switch (index) { + case 2: + eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,0); + interface::popScreen(); + break; + case 3: + eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,1); + interface::popScreen(); + break; + } +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 87c9d47..468fe5e 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -631,10 +631,33 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class PreheatDuringEstimateMenu: public Menu { +public: + PreheatDuringEstimateMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + +class OverrideGCodeTempMenu: public Menu { +public: + OverrideGCodeTempMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; class BuildSettingsMenu: public Menu { private: - ABPCopiesSetScreen abpCopiesSetScreen; + PreheatDuringEstimateMenu preheatDuringEstimateMenu; + OverrideGCodeTempMenu overrideGCodeTempMenu; + ABPCopiesSetScreen abpCopiesSetScreen; public: BuildSettingsMenu(); From 29b90ab2b1d486090b959d9b95cc24fb1e59ee2f Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Tue, 24 Jan 2012 16:20:42 -0700 Subject: [PATCH 38/61] Z+ button now advances the Build Phase Scroller --- firmware/src/shared/Menu.cc | 83 ++++++++++++++++++++++--------------- firmware/src/shared/Menu.hh | 26 ++++++++++-- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 3414583..a963aaa 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1093,8 +1093,9 @@ void SnakeMode::notifyButtonPressed(ButtonArray::ButtonName button) { void MonitorMode::reset() { - updatePhase = 0; - buildTimePhase = 0; + updatePhase = UPDATE_PHASE_FIRST; + buildTimePhase = BUILD_TIME_PHASE_FIRST; + lastBuildTimePhase = BUILD_TIME_PHASE_FIRST; lastElapsedSeconds = 0.0; pausePushLockout = false; pauseMode.autoPause = false; @@ -1231,7 +1232,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { // Redraw tool info switch (updatePhase) { - case 0: + case UPDATE_PHASE_TOOL_TEMP: lcd.setCursor(6,2); if (extruderControl(SLAVE_CMD_GET_TEMP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); @@ -1241,7 +1242,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { } break; - case 1: + case UPDATE_PHASE_TOOL_TEMP_SET_POINT: lcd.setCursor(10,2); if (extruderControl(SLAVE_CMD_GET_SP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); @@ -1251,7 +1252,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { } break; - case 2: + case UPDATE_PHASE_PLATFORM_TEMP: lcd.setCursor(6,3); if (extruderControl(SLAVE_CMD_GET_PLATFORM_TEMP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); @@ -1261,7 +1262,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { } break; - case 3: + case UPDATE_PHASE_PLATFORM_SET_POINT: lcd.setCursor(10,3); if (extruderControl(SLAVE_CMD_GET_PLATFORM_SP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); @@ -1274,7 +1275,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( command::getPauseAtZPos() == 0 ) lcd.write(' '); else lcd.write('*'); break; - case 4: + case UPDATE_PHASE_BUILD_PHASE_SCROLLER: enum host::HostState hostState = host::getHostState(); if ( (hostState != host::HOST_STATE_BUILDING ) && ( hostState != host::HOST_STATE_BUILDING_FROM_SD )) break; @@ -1285,23 +1286,10 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { Motherboard::getBoard().buzz(2, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); } - //Holding the zero button stops rotation - if ( ! interface::isButtonPressed(ButtonArray::OK) ) { - buildTimePhase ++; + bool okButtonHeld = interface::isButtonPressed(ButtonArray::OK); - //Skip Time Left if we skipped the estimation - if (( buildDuration == 0 ) && ( buildTimePhase == 2 )) buildTimePhase ++; - } - - //If we're setup to print more than one copy, then show that build phase, - //otherwise skip it - uint8_t totalCopies; - if ( buildTimePhase == 5 ) { - totalCopies = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); - if ( totalCopies <= 1 ) buildTimePhase ++; - } - - if ( buildTimePhase >= 6 ) buildTimePhase = 0; + //Holding the ok button stops rotation + if ( okButtonHeld ) buildTimePhase = lastBuildTimePhase; float secs; int32_t tsecs; @@ -1311,7 +1299,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { float filamentUsed, lastFilamentUsed; switch (buildTimePhase) { - case 0: //Completed Percent + case BUILD_TIME_PHASE_COMPLETED_PERCENT: lcd.setCursor(0,1); lcd.writeFromPgmspace(completed_percent); lcd.setCursor(11,1); @@ -1324,7 +1312,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { strcat(buf, "% "); lcd.writeString(buf); break; - case 1: //Elapsed Time + case BUILD_TIME_PHASE_ELAPSED_TIME: lcd.setCursor(0,1); lcd.writeFromPgmspace(elapsed_time); lcd.setCursor(9,1); @@ -1338,7 +1326,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { appendTime(buf, sizeof(buf), (uint32_t)secs); lcd.writeString(buf); break; - case 2: // Time Left + case BUILD_TIME_PHASE_TIME_LEFT: lcd.setCursor(0,1); if (( timeLeftDisplayed ) || ( command::getFilamentLength() >= 1 )) { lcd.writeFromPgmspace(time_left); @@ -1362,7 +1350,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeString(buf); } break; - case 3: // Zpos + case BUILD_TIME_PHASE_ZPOS: lcd.setCursor(0,1); lcd.writeFromPgmspace(zpos); lcd.setCursor(6,1); @@ -1374,7 +1362,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeFromPgmspace(zpos_mm); break; - case 4: // Filament + case BUILD_TIME_PHASE_FILAMENT: lcd.setCursor(0,1); lcd.writeFromPgmspace(filament); lcd.setCursor(9,1); @@ -1393,7 +1381,8 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( precision == 1 ) lcd.write('m'); lcd.write('m'); break; - case 5: // Copies printed + case BUILD_TIME_PHASE_COPIES_PRINTED: + uint8_t totalCopies = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); lcd.setCursor(0,1); lcd.writeFromPgmspace(copies); lcd.setCursor(7,1); @@ -1402,13 +1391,34 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeFloat((float)totalCopies, 0); break; } + + if ( ! okButtonHeld ) { + //Advance buildTimePhase and wrap around + lastBuildTimePhase = buildTimePhase; + buildTimePhase = (enum BuildTimePhase)((uint8_t)buildTimePhase + 1); + + //Skip Time left if we skipped the estimation + if (( buildTimePhase == BUILD_TIME_PHASE_TIME_LEFT ) && ( buildDuration == 0 )) + buildTimePhase = (enum BuildTimePhase)((uint8_t)buildTimePhase + 1); + + //If we're setup to print more than one copy, then show that build phase, + //otherwise skip it + if ( buildTimePhase == BUILD_TIME_PHASE_COPIES_PRINTED ) { + uint8_t totalCopies = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); + if ( totalCopies <= 1 ) + buildTimePhase = (enum BuildTimePhase)((uint8_t)buildTimePhase + 1); + } + + if ( buildTimePhase >= BUILD_TIME_PHASE_LAST ) + buildTimePhase = BUILD_TIME_PHASE_FIRST; + } break; } - updatePhase++; - if (updatePhase > 6) { - updatePhase = 0; - } + //Advance updatePhase and wrap around + updatePhase = (enum UpdatePhase)((uint8_t)updatePhase + 1); + if (updatePhase >= UPDATE_PHASE_LAST) + updatePhase = UPDATE_PHASE_FIRST; } void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { @@ -1424,8 +1434,13 @@ void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { interface::popScreen(); break; } + case ButtonArray::ZPLUS: + if ( host::getHostState() == host::HOST_STATE_BUILDING_FROM_SD ) + updatePhase = UPDATE_PHASE_BUILD_PHASE_SCROLLER; + break; case ButtonArray::OK: - if (( estimatingBuild ) && ( host::getHostState() == host::HOST_STATE_ESTIMATING_FROM_SD )) { + //Skip build if user hit the button during the estimation phase + if (( host::getHostState() == host::HOST_STATE_ESTIMATING_FROM_SD ) && ( estimatingBuild )) { buildDuration = 0; host::setHostStateBuildingFromSD(); command::setEstimation(false); diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 468fe5e..38a87c7 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -253,13 +253,33 @@ private: bool printAnotherEnabled; }; - class MonitorMode: public Screen { private: CancelBuildMenu cancelBuildMenu; - uint8_t updatePhase; - uint8_t buildTimePhase; + enum UpdatePhase { + UPDATE_PHASE_FIRST = 0, + UPDATE_PHASE_TOOL_TEMP = UPDATE_PHASE_FIRST, + UPDATE_PHASE_TOOL_TEMP_SET_POINT, + UPDATE_PHASE_PLATFORM_TEMP, + UPDATE_PHASE_PLATFORM_SET_POINT, + UPDATE_PHASE_BUILD_PHASE_SCROLLER, + UPDATE_PHASE_LAST //Not counted, just an end marker + }; + + enum BuildTimePhase { + BUILD_TIME_PHASE_FIRST = 0, + BUILD_TIME_PHASE_COMPLETED_PERCENT = BUILD_TIME_PHASE_FIRST, + BUILD_TIME_PHASE_ELAPSED_TIME, + BUILD_TIME_PHASE_TIME_LEFT, + BUILD_TIME_PHASE_ZPOS, + BUILD_TIME_PHASE_FILAMENT, + BUILD_TIME_PHASE_COPIES_PRINTED, + BUILD_TIME_PHASE_LAST //Not counted, just an end marker + }; + + enum UpdatePhase updatePhase; + enum BuildTimePhase buildTimePhase, lastBuildTimePhase; float lastElapsedSeconds; PauseMode pauseMode; bool pausePushLockout; From c45db419a21e4f5afcd43f5c4d453d56cf17091a Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Tue, 24 Jan 2012 16:51:25 -0700 Subject: [PATCH 39/61] Added "Trip Odometer" to "Filament Used" menu --- firmware/src/Motherboard/EepromMap.cc | 1 + firmware/src/Motherboard/EepromMap.hh | 1 + firmware/src/shared/Menu.cc | 34 ++++++++++++++++++++++----- firmware/src/shared/Menu.hh | 2 ++ 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index dfd350d..0d7970b 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -47,6 +47,7 @@ void setDefaults() { putEepromInt64(eeprom::STEPS_PER_MM_A,STEPS_PER_MM_A_DEFAULT); putEepromInt64(eeprom::STEPS_PER_MM_B,STEPS_PER_MM_B_DEFAULT); putEepromInt64(eeprom::FILAMENT_USED,0); + putEepromInt64(eeprom::FILAMENT_USED_TRIP,0); eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,1); eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,0); eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,0); diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index 9b0e7ae..4d65aef 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -84,6 +84,7 @@ const static uint16_t STEPS_PER_MM_B = 0x00AB; //int64_t (8 bytes) The filament used in steps const static uint16_t FILAMENT_USED = 0x00B3; +const static uint16_t FILAMENT_USED_TRIP = 0x00BB; //Number of ABP copies (1-254) when building from SDCard (1 byte) const static uint16_t ABP_COPIES = 0x00C3; diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index a963aaa..d15a1c5 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -3375,6 +3375,7 @@ void FilamentUsedResetMenu::handleSelect(uint8_t index) { case 3: //Reset to zero eeprom::putEepromInt64(eeprom::FILAMENT_USED, 0); + eeprom::putEepromInt64(eeprom::FILAMENT_USED_TRIP, 0); case 2: interface::popScreen(); interface::popScreen(); @@ -3383,19 +3384,30 @@ void FilamentUsedResetMenu::handleSelect(uint8_t index) { } void FilamentUsedMode::reset() { + lifetimeDisplay = true; + overrideForceRedraw = false; } void FilamentUsedMode::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar message1[] = "Filament Used:"; - const static PROGMEM prog_uchar message3[] = " (reset)"; + const static PROGMEM prog_uchar lifetime[] = "Lifetime Odo.:"; + const static PROGMEM prog_uchar trip[] = "Trip Odometer:"; + const static PROGMEM prog_uchar but_life[] = "(trip) (reset)"; + const static PROGMEM prog_uchar but_trip[] = "(life) (reset)"; - if (forceRedraw) { + if ((forceRedraw) || (overrideForceRedraw)) { lcd.clear(); lcd.setCursor(0,0); - lcd.writeFromPgmspace(message1); + if ( lifetimeDisplay ) lcd.writeFromPgmspace(lifetime); + else lcd.writeFromPgmspace(trip); int64_t filamentUsed = eeprom::getEepromInt64(eeprom::FILAMENT_USED, 0); + + if ( ! lifetimeDisplay ) { + int64_t trip = eeprom::getEepromInt64(eeprom::FILAMENT_USED_TRIP, 0); + filamentUsed = filamentUsed - trip; + } + float filamentUsedMM = stepsToMM(filamentUsed, AXIS_A); lcd.setCursor(0,1); @@ -3403,11 +3415,14 @@ void FilamentUsedMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.write('m'); lcd.setCursor(0,2); - lcd.writeFromPgmspace(message3); + if ( lifetimeDisplay ) lcd.writeFromPgmspace(but_life); + else lcd.writeFromPgmspace(but_trip); lcd.setCursor(0,3); lcd.writeFloat(((filamentUsedMM / 25.4) / 12.0), 4); lcd.writeString("ft"); + + overrideForceRedraw = false; } } @@ -3417,9 +3432,16 @@ void FilamentUsedMode::notifyButtonPressed(ButtonArray::ButtonName button) { interface::popScreen(); break; case ButtonArray::ZERO: + lifetimeDisplay ^= true; + overrideForceRedraw = true; break; case ButtonArray::OK: - interface::pushScreen(&filamentUsedResetMenu); + if ( lifetimeDisplay ) + interface::pushScreen(&filamentUsedResetMenu); + else { + eeprom::putEepromInt64(eeprom::FILAMENT_USED_TRIP, eeprom::getEepromInt64(eeprom::FILAMENT_USED, 0)); + interface::popScreen(); + } break; case ButtonArray::ZPLUS: case ButtonArray::ZMINUS: diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 38a87c7..cf0d384 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -627,6 +627,8 @@ class FilamentUsedMode: public Screen { private: FilamentUsedResetMenu filamentUsedResetMenu; + bool overrideForceRedraw; + bool lifetimeDisplay; public: micros_t getUpdateRate() {return 100L * 1000L;} From e7446f58c9fa10c2dc0627517272f08d666d288d Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Tue, 24 Jan 2012 17:48:09 -0700 Subject: [PATCH 40/61] Fixed bug with ATX Power Good Reset that caused previous build name to be displayed when switched on if user switched off during or at the completion of a build. --- firmware/src/Motherboard/Host.cc | 7 +++++++ firmware/src/Motherboard/Host.hh | 3 +++ firmware/src/Motherboard/Main.cc | 1 + 3 files changed, 11 insertions(+) diff --git a/firmware/src/Motherboard/Host.cc b/firmware/src/Motherboard/Host.cc index 279f81c..d82038e 100644 --- a/firmware/src/Motherboard/Host.cc +++ b/firmware/src/Motherboard/Host.cc @@ -582,6 +582,13 @@ void stopBuild() { do_host_reset = true; // indicate reset after response has been sent } +// Reset the current build, used for ATX power on reset +void resetBuild() { + machineName[0] = 0; + buildName[0] = 0; + currentState = HOST_STATE_READY; +} + bool isBuildComplete() { if (( command::isEmpty() ) && ( ! sdcard::playbackHasNext() )) return true; return false; diff --git a/firmware/src/Motherboard/Host.hh b/firmware/src/Motherboard/Host.hh index afd2fb4..8bb3c9f 100644 --- a/firmware/src/Motherboard/Host.hh +++ b/firmware/src/Motherboard/Host.hh @@ -64,6 +64,9 @@ sdcard::SdErrorCode startBuildFromSD(bool estimateFirst); /// Stop the current build void stopBuild(); +/// Reset the current build (used for ATX Power reset) +void resetBuild(); + /// Returns true if the build is completed bool isBuildComplete(); diff --git a/firmware/src/Motherboard/Main.cc b/firmware/src/Motherboard/Main.cc index 9e8fc7b..4bd6485 100644 --- a/firmware/src/Motherboard/Main.cc +++ b/firmware/src/Motherboard/Main.cc @@ -104,6 +104,7 @@ int main() { /// reset bool powerGood = ATX_POWER_GOOD.getValue(); if (( ! atxLastPowerGood ) && ( powerGood )) { + host::resetBuild(); reset(true); } atxLastPowerGood = powerGood; From 712c8357084180fdcd34ba9ad05d186915a70437 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 25 Jan 2012 12:18:23 -0700 Subject: [PATCH 41/61] Added 4 profiles which can save X/Y/Z Home Offsets and Platform / Extruder Temps and Extruder RPM. This enables quick switching from HBP to ABP and different plastic types. --- firmware/src/Motherboard/EepromMap.hh | 13 + firmware/src/shared/Menu.cc | 388 +++++++++++++++++++++++++- firmware/src/shared/Menu.hh | 65 +++++ 3 files changed, 451 insertions(+), 15 deletions(-) diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index 4d65aef..a5af0f8 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -96,6 +96,19 @@ const static uint16_t PREHEAT_DURING_ESTIMATE = 0x00C4; //0 = Disable, 1 = Enabled const static uint16_t OVERRIDE_GCODE_TEMP = 0x00C5; +//Profiles +#define PROFILE_NAME_LENGTH 8 +#define PROFILE_HOME_OFFSETS_SIZE (4 * 3) //X, Y, Z (uint32_t) + +#define PROFILE_NEXT_OFFSET (PROFILE_NAME_LENGTH + \ + PROFILE_HOME_OFFSETS_SIZE + \ + 4 ) //24 (0x18) 4=Bytes (Hbp, tool0, tool1, extruder) + +//4 Profiles = 0x00C6 + PROFILE_NEXT_OFFSET * 4 +const static uint16_t PROFILE_BASE = 0x00C6; + +//Next free location: 0x126 + /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index d15a1c5..daca3a2 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1737,7 +1737,7 @@ int64_t MainMenu::checkAndGetEepromDefault(const uint16_t location, const int64_ } MainMenu::MainMenu() { - itemCount = 19; + itemCount = 20; reset(); //Read in the axisStepsPerMM, we'll need these for various firmware functions later on @@ -1762,6 +1762,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar moodlight[] = "Mood Light"; const static PROGMEM prog_uchar buzzer[] = "Buzzer"; const static PROGMEM prog_uchar buildSettings[] = "Build Settings"; + const static PROGMEM prog_uchar profiles[] = "Profiles"; const static PROGMEM prog_uchar extruderFan[] = "Extruder Fan"; const static PROGMEM prog_uchar calibrate[] = "Calibrate"; const static PROGMEM prog_uchar homeOffsets[] = "Home Offsets"; @@ -1806,27 +1807,30 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(buildSettings); break; case 11: - lcd.writeFromPgmspace(extruderFan); + lcd.writeFromPgmspace(profiles); break; case 12: - lcd.writeFromPgmspace(calibrate); + lcd.writeFromPgmspace(extruderFan); break; case 13: - lcd.writeFromPgmspace(homeOffsets); + lcd.writeFromPgmspace(calibrate); break; case 14: - lcd.writeFromPgmspace(filamentUsed); + lcd.writeFromPgmspace(homeOffsets); break; case 15: - lcd.writeFromPgmspace(endStops); + lcd.writeFromPgmspace(filamentUsed); break; case 16: - lcd.writeFromPgmspace(stepsPerMm); + lcd.writeFromPgmspace(endStops); break; case 17: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(stepsPerMm); break; case 18: + lcd.writeFromPgmspace(versions); + break; + case 19: lcd.writeFromPgmspace(snake); break; } @@ -1880,34 +1884,38 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&buildSettingsMenu); break; case 11: + // Show Profiles Menu + interface::pushScreen(&profilesMenu); + break; + case 12: // Show Extruder Fan Mode interface::pushScreen(&extruderFanMenu); break; - case 12: + case 13: // Show Calibrate Mode interface::pushScreen(&calibrateMode); break; - case 13: + case 14: // Show Home Offsets Mode interface::pushScreen(&homeOffsetsMode); break; - case 14: + case 15: // Show Filament Used Mode interface::pushScreen(&filamentUsedMode); break; - case 15: + case 16: // Show test end stops menu interface::pushScreen(&testEndStopsMode); break; - case 16: + case 17: // Show steps per mm menu interface::pushScreen(&stepsPerMMMode); break; - case 17: + case 18: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 18: + case 19: // Show build from SD screen interface::pushScreen(&snake); break; @@ -3662,4 +3670,354 @@ void OverrideGCodeTempMenu::handleSelect(uint8_t index) { } } +#define NUM_PROFILES 4 +#define PROFILES_SAVED_AXIS 3 + +void writeProfileToEeprom(uint8_t pIndex, uint8_t *pName, int32_t homeX, + int32_t homeY, int32_t homeZ, uint8_t hbpTemp, + uint8_t tool0Temp, uint8_t tool1Temp, uint8_t extruderRpm) { + uint16_t offset = eeprom::PROFILE_BASE + (uint16_t)pIndex * PROFILE_NEXT_OFFSET; + + cli(); + + //Write profile name + if ( pName ) eeprom_write_block(pName,(uint8_t*)offset, PROFILE_NAME_LENGTH); + offset += PROFILE_NAME_LENGTH; + + //Write home axis + eeprom_write_block(&homeX, (void*) offset, 4); offset += 4; + eeprom_write_block(&homeY, (void*) offset, 4); offset += 4; + eeprom_write_block(&homeZ, (void*) offset, 4); offset += 4; + + //Write temps and extruder RPM + eeprom_write_byte((uint8_t *)offset, hbpTemp); offset += 1; + eeprom_write_byte((uint8_t *)offset, tool0Temp); offset += 1; + eeprom_write_byte((uint8_t *)offset, tool1Temp); offset += 1; + eeprom_write_byte((uint8_t *)offset, extruderRpm); offset += 1; + + sei(); +} + +void readProfileFromEeprom(uint8_t pIndex, uint8_t *pName, int32_t *homeX, + int32_t *homeY, int32_t *homeZ, uint8_t *hbpTemp, + uint8_t *tool0Temp, uint8_t *tool1Temp, uint8_t *extruderRpm) { + uint16_t offset = eeprom::PROFILE_BASE + (uint16_t)pIndex * PROFILE_NEXT_OFFSET; + + cli(); + + //Read profile name + if ( pName ) eeprom_read_block(pName,(uint8_t*)offset, PROFILE_NAME_LENGTH); + offset += PROFILE_NAME_LENGTH; + + //Write home axis + eeprom_read_block(homeX, (void*) offset, 4); offset += 4; + eeprom_read_block(homeY, (void*) offset, 4); offset += 4; + eeprom_read_block(homeZ, (void*) offset, 4); offset += 4; + + //Write temps and extruder RPM + *hbpTemp = eeprom_read_byte((uint8_t *)offset); offset += 1; + *tool0Temp = eeprom_read_byte((uint8_t *)offset); offset += 1; + *tool1Temp = eeprom_read_byte((uint8_t *)offset); offset += 1; + *extruderRpm = eeprom_read_byte((uint8_t *)offset); offset += 1; + + sei(); +} + +void getProfileName(uint8_t pIndex, uint8_t *buf) { + uint16_t offset = eeprom::PROFILE_BASE + PROFILE_NEXT_OFFSET * (uint16_t)pIndex; + + cli(); + eeprom_read_block(buf,(void *)offset,PROFILE_NAME_LENGTH); + sei(); + + buf[PROFILE_NAME_LENGTH] = '\0'; +} + +ProfilesMenu::ProfilesMenu() { + itemCount = NUM_PROFILES; + reset(); + + //Setup defaults if required + //If the value is 0xff, write the profile number + uint8_t buf[PROFILE_NAME_LENGTH]; + + const static PROGMEM prog_uchar defaultProfile[] = "Profile?"; + + //Get the home axis positions, we may need this to write the defaults + homePosition = steppers::getPosition(); + + for (uint8_t i = 0; i < PROFILES_SAVED_AXIS; i++) { + uint16_t offset = eeprom::AXIS_HOME_POSITIONS + 4*(uint16_t)i; + cli(); + eeprom_read_block(&homePosition[i], (void*)offset, 4); + sei(); + } + + for (int i = 0; i < NUM_PROFILES; i ++ ) { + uint8_t value = eeprom_read_byte((uint8_t *)eeprom::PROFILE_BASE + (uint16_t)i * PROFILE_HOME_OFFSETS_SIZE); + + //If set to 255, create the default profile name + if ( value == 0xff ) { + //Create the default profile name + for( uint8_t i = 0; i < PROFILE_NAME_LENGTH; i ++ ) + buf[i] = pgm_read_byte_near(defaultProfile+i); + buf[PROFILE_NAME_LENGTH - 1] = '1' + i; + + //Write the defaults + writeProfileToEeprom(i, buf, homePosition[0], homePosition[1], homePosition[2], + 100, 210, 210, 19); + } + } +} + +void ProfilesMenu::resetState() { + firstItemIndex = 0; + itemIndex = 0; +} + +void ProfilesMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + uint8_t buf[PROFILE_NAME_LENGTH + 1]; + + getProfileName(index, buf); + + lcd.writeString((char *)buf); +} + +void ProfilesMenu::handleSelect(uint8_t index) { + profileSubMenu.profileIndex = index; + interface::pushScreen(&profileSubMenu); +} + +ProfileSubMenu::ProfileSubMenu() { + itemCount = 4; + reset(); +} + +void ProfileSubMenu::resetState() { + itemIndex = 0; + firstItemIndex = 0; +} + +void ProfileSubMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg1[] = "Restore"; + const static PROGMEM prog_uchar msg2[] = "Display Config"; + const static PROGMEM prog_uchar msg3[] = "Change Name"; + const static PROGMEM prog_uchar msg4[] = "Save To Profile"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg1); + break; + case 1: + lcd.writeFromPgmspace(msg2); + break; + case 2: + lcd.writeFromPgmspace(msg3); + break; + case 3: + lcd.writeFromPgmspace(msg4); + break; + } +} + +void ProfileSubMenu::handleSelect(uint8_t index) { + uint8_t hbpTemp, tool0Temp, tool1Temp, extruderRpm; + + switch (index) { + case 0: + //Restore + //Read settings from eeprom + readProfileFromEeprom(profileIndex, NULL, &homePosition[0], &homePosition[1], &homePosition[2], + &hbpTemp, &tool0Temp, &tool1Temp, &extruderRpm); + + //Write out the home offsets + for (uint8_t i = 0; i < PROFILES_SAVED_AXIS; i++) { + uint16_t offset = eeprom::AXIS_HOME_POSITIONS + 4*(uint16_t)i; + cli(); + eeprom_write_block(&homePosition[i], (void*)offset, 4); + sei(); + } + + cli(); + eeprom_write_byte((uint8_t *)eeprom::PLATFORM_TEMP, hbpTemp); + eeprom_write_byte((uint8_t *)eeprom::TOOL0_TEMP, tool0Temp); + eeprom_write_byte((uint8_t *)eeprom::TOOL1_TEMP, tool1Temp); + eeprom_write_byte((uint8_t *)eeprom::EXTRUDE_RPM, extruderRpm); + sei(); + + interface::popScreen(); + interface::popScreen(); + + //Reset + host::stopBuild(); + break; + case 1: + //Display settings + profileDisplaySettingsMenu.profileIndex = profileIndex; + interface::pushScreen(&profileDisplaySettingsMenu); + break; + case 2: + //Change Profile Name + profileChangeNameMode.profileIndex = profileIndex; + interface::pushScreen(&profileChangeNameMode); + break; + case 3: //Save To Profile + //Get the home axis positions + homePosition = steppers::getPosition(); + for (uint8_t i = 0; i < PROFILES_SAVED_AXIS; i++) { + uint16_t offset = eeprom::AXIS_HOME_POSITIONS + 4*(uint16_t)i; + cli(); + eeprom_read_block(&homePosition[i], (void*)offset, 4); + sei(); + } + + hbpTemp = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110); + tool0Temp = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220); + tool1Temp = eeprom::getEeprom8(eeprom::TOOL1_TEMP, 220); + extruderRpm = eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19); + + writeProfileToEeprom(profileIndex, NULL, homePosition[0], homePosition[1], homePosition[2], + hbpTemp, tool0Temp, tool1Temp, extruderRpm); + + interface::popScreen(); + break; + } +} + +void ProfileChangeNameMode::reset() { + cursorLocation = 0; + getProfileName(profileIndex, profileName); +} + +void ProfileChangeNameMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Profile Name:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + lcd.setCursor(0,1); + lcd.writeString((char *)profileName); + + //Draw the cursor + lcd.setCursor(cursorLocation,2); + lcd.write('^'); + + //Write a blank before and after the cursor if we're not at the ends + if ( cursorLocation >= 1 ) { + lcd.setCursor(cursorLocation-1, 2); + lcd.writeFromPgmspace(blank); + } + if ( cursorLocation < PROFILE_NAME_LENGTH ) { + lcd.setCursor(cursorLocation+1, 2); + lcd.writeFromPgmspace(blank); + } +} + +void ProfileChangeNameMode::notifyButtonPressed(ButtonArray::ButtonName button) { + uint16_t offset; + + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + //Write the profile name + offset = eeprom::PROFILE_BASE + (uint16_t)profileIndex * PROFILE_NEXT_OFFSET; + + cli(); + eeprom_write_block(profileName,(uint8_t*)offset, PROFILE_NAME_LENGTH); + sei(); + + interface::popScreen(); + break; + case ButtonArray::YPLUS: + profileName[cursorLocation] += 1; + break; + case ButtonArray::ZPLUS: + profileName[cursorLocation] += 5; + break; + case ButtonArray::YMINUS: + profileName[cursorLocation] -= 1; + break; + case ButtonArray::ZMINUS: + profileName[cursorLocation] -= 5; + break; + case ButtonArray::XMINUS: + if ( cursorLocation > 0 ) cursorLocation --; + break; + case ButtonArray::XPLUS: + if ( cursorLocation < (PROFILE_NAME_LENGTH-1) ) cursorLocation ++; + break; + } + + //Hard limits + if ( profileName[cursorLocation] < 32 ) profileName[cursorLocation] = 32; + if ( profileName[cursorLocation] > 126 ) profileName[cursorLocation] = 126; +} + +ProfileDisplaySettingsMenu::ProfileDisplaySettingsMenu() { + itemCount = 8; + reset(); +} + +void ProfileDisplaySettingsMenu::resetState() { + readProfileFromEeprom(profileIndex, profileName, &homeX, &homeY, &homeZ, + &hbpTemp, &tool0Temp, &tool1Temp, &extruderRpm); + itemIndex = 2; + firstItemIndex = 2; +} + +void ProfileDisplaySettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar xOffset[] = "XOff: "; + const static PROGMEM prog_uchar yOffset[] = "YOff: "; + const static PROGMEM prog_uchar zOffset[] = "ZOff: "; + const static PROGMEM prog_uchar hbp[] = "HBP Temp: "; + const static PROGMEM prog_uchar tool0[] = "Tool0 Temp: "; + const static PROGMEM prog_uchar extruder[] = "ExtrdrRPM: "; + + switch (index) { + case 0: + lcd.writeString((char *)profileName); + break; + case 2: + lcd.writeFromPgmspace(xOffset); + lcd.writeFloat(stepsToMM(homeX, AXIS_X), 3); + break; + case 3: + lcd.writeFromPgmspace(yOffset); + lcd.writeFloat(stepsToMM(homeY, AXIS_Y), 3); + break; + case 4: + lcd.writeFromPgmspace(zOffset); + lcd.writeFloat(stepsToMM(homeZ, AXIS_Z), 3); + break; + case 5: + lcd.writeFromPgmspace(hbp); + lcd.writeFloat((float)hbpTemp, 0); + break; + case 6: + lcd.writeFromPgmspace(tool0); + lcd.writeFloat((float)tool0Temp, 0); + break; + case 7: + lcd.writeFromPgmspace(extruder); + lcd.writeFloat((float)extruderRpm / 10.0, 1); + break; + } +} + +void ProfileDisplaySettingsMenu::handleSelect(uint8_t index) { +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index cf0d384..2fb0b11 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -690,6 +690,70 @@ protected: void handleSelect(uint8_t index); }; +class ProfileChangeNameMode: public Screen { +private: + uint8_t cursorLocation; + uint8_t profileName[8+1]; + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); + + uint8_t profileIndex; +}; + +class ProfileDisplaySettingsMenu: public Menu { +private: + uint8_t profileName[8+1]; + int32_t homeX, homeY, homeZ; + uint8_t hbpTemp, tool0Temp, tool1Temp, extruderRpm; +public: + ProfileDisplaySettingsMenu(); + + void resetState(); + + uint8_t profileIndex; +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + +class ProfileSubMenu: public Menu { +private: + ProfileChangeNameMode profileChangeNameMode; + ProfileDisplaySettingsMenu profileDisplaySettingsMenu; + +public: + ProfileSubMenu(); + + void resetState(); + + uint8_t profileIndex; +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + +class ProfilesMenu: public Menu { +private: + ProfileSubMenu profileSubMenu; +public: + ProfilesMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -711,6 +775,7 @@ private: AdvanceABPMode advanceABPMode; BuzzerSetRepeatsMode buzzerSetRepeats; BuildSettingsMenu buildSettingsMenu; + ProfilesMenu profilesMenu; ExtruderFanMenu extruderFanMenu; CalibrateMode calibrateMode; HomeOffsetsMode homeOffsetsMode; From af813b639e7a7b584fae96ae520841c70e4765bf Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 25 Jan 2012 12:21:31 -0700 Subject: [PATCH 42/61] Corrected labels in HomeOffsets from steps to mm to reflect recent change. --- firmware/src/shared/Menu.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index daca3a2..7bdade8 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -2990,9 +2990,9 @@ void HomeOffsetsMode::reset() { } void HomeOffsetsMode::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar message1x[] = "X Offset(steps):"; - const static PROGMEM prog_uchar message1y[] = "Y Offset(steps):"; - const static PROGMEM prog_uchar message1z[] = "Z Offset(steps):"; + const static PROGMEM prog_uchar message1x[] = "X Offset:"; + const static PROGMEM prog_uchar message1y[] = "Y Offset:"; + const static PROGMEM prog_uchar message1z[] = "Z Offset:"; const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; const static PROGMEM prog_uchar blank[] = " "; const static PROGMEM prog_uchar mm[] = "mm"; From 53633df25e1bf5e6ddef5f5ef2740ab3498df7b9 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 25 Jan 2012 16:43:09 -0700 Subject: [PATCH 43/61] Fixed mispelt word --- firmware/src/shared/Menu.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 7bdade8..1e95cd2 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -3637,7 +3637,7 @@ void OverrideGCodeTempMenu::resetState() { void OverrideGCodeTempMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar msg1[] = "Override GCode"; - const static PROGMEM prog_uchar msg2[] = "Termperature:"; + const static PROGMEM prog_uchar msg2[] = "Temperature:"; const static PROGMEM prog_uchar disable[] = "Disable"; const static PROGMEM prog_uchar enable[] = "Enable"; From 2b890bf66d4391ac0e65b2426ce8fbb62cb1dd11 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Wed, 25 Jan 2012 17:57:05 -0700 Subject: [PATCH 44/61] Improved checking for invalid / corrupt profile names --- firmware/src/shared/Menu.cc | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 1e95cd2..50002d9 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -3723,6 +3723,8 @@ void readProfileFromEeprom(uint8_t pIndex, uint8_t *pName, int32_t *homeX, sei(); } +//buf should have length PROFILE_NAME_LENGTH + 1 + void getProfileName(uint8_t pIndex, uint8_t *buf) { uint16_t offset = eeprom::PROFILE_BASE + PROFILE_NEXT_OFFSET * (uint16_t)pIndex; @@ -3733,6 +3735,20 @@ void getProfileName(uint8_t pIndex, uint8_t *buf) { buf[PROFILE_NAME_LENGTH] = '\0'; } +#define NAME_CHAR_LOWER_LIMIT 32 +#define NAME_CHAR_UPPER_LIMIT 126 + +bool isValidProfileName(uint8_t pIndex) { + uint8_t buf[PROFILE_NAME_LENGTH + 1]; + + getProfileName(pIndex, buf); + for ( uint8_t i = 0; i < PROFILE_NAME_LENGTH; i ++ ) { + if (( buf[i] < NAME_CHAR_LOWER_LIMIT ) || ( buf[i] > NAME_CHAR_UPPER_LIMIT ) || ( buf[i] == 0xff )) return false; + } + + return true; +} + ProfilesMenu::ProfilesMenu() { itemCount = NUM_PROFILES; reset(); @@ -3754,10 +3770,7 @@ ProfilesMenu::ProfilesMenu() { } for (int i = 0; i < NUM_PROFILES; i ++ ) { - uint8_t value = eeprom_read_byte((uint8_t *)eeprom::PROFILE_BASE + (uint16_t)i * PROFILE_HOME_OFFSETS_SIZE); - - //If set to 255, create the default profile name - if ( value == 0xff ) { + if ( ! isValidProfileName(i)) { //Create the default profile name for( uint8_t i = 0; i < PROFILE_NAME_LENGTH; i ++ ) buf[i] = pgm_read_byte_near(defaultProfile+i); @@ -3962,8 +3975,8 @@ void ProfileChangeNameMode::notifyButtonPressed(ButtonArray::ButtonName button) } //Hard limits - if ( profileName[cursorLocation] < 32 ) profileName[cursorLocation] = 32; - if ( profileName[cursorLocation] > 126 ) profileName[cursorLocation] = 126; + if ( profileName[cursorLocation] < NAME_CHAR_LOWER_LIMIT ) profileName[cursorLocation] = NAME_CHAR_LOWER_LIMIT; + if ( profileName[cursorLocation] > NAME_CHAR_UPPER_LIMIT ) profileName[cursorLocation] = NAME_CHAR_UPPER_LIMIT; } ProfileDisplaySettingsMenu::ProfileDisplaySettingsMenu() { From 4f4adb14e0ef6b17853c7322353b2834ddc23995 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Mon, 30 Jan 2012 17:52:12 -0700 Subject: [PATCH 45/61] Changed UI for Mood Light to use Y+/Y-/Z+/Z- to be more consistant with the reset of the interface --- firmware/src/shared/Menu.cc | 47 +++++++++++++++------- firmware/src/shared/Menu.hh | 2 + firmware/src/shared/MoodLightController.cc | 13 +++++- firmware/src/shared/MoodLightController.hh | 2 + 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 50002d9..4f52c64 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -777,12 +777,13 @@ void ExtruderSetRpmScreen::notifyButtonPressed(ButtonArray::ButtonName button) { void MoodLightMode::reset() { updatePhase = 0; + scriptId = eeprom_read_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT); } void MoodLightMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar mood1[] = "Mood: "; const static PROGMEM prog_uchar mood3_1[] = "(set RGB)"; - const static PROGMEM prog_uchar mood3_2[] = "(mood)"; + const static PROGMEM prog_uchar msg4[] = "Up/Dn/Ent to Set"; const static PROGMEM prog_uchar blank[] = " "; const static PROGMEM prog_uchar moodNotPresent1[] = "Mood Light not"; const static PROGMEM prog_uchar moodNotPresent2[] = "present!!"; @@ -814,12 +815,11 @@ void MoodLightMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,0); lcd.writeFromPgmspace(mood1); - lcd.setCursor(10,2); - lcd.writeFromPgmspace(mood3_2); + lcd.setCursor(0,3); + lcd.writeFromPgmspace(msg4); } //Redraw tool info - uint8_t scriptId = eeprom_read_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT); switch (updatePhase) { case 0: @@ -845,38 +845,57 @@ void MoodLightMode::update(LiquidCrystal& lcd, bool forceRedraw) { void MoodLightMode::notifyButtonPressed(ButtonArray::ButtonName button) { - uint8_t scriptId; - if ( ! interface::moodLightController().blinkM.blinkMIsPresent ) interface::popScreen(); + uint8_t i; + switch (button) { case ButtonArray::OK: - //Change the script to the next script id - scriptId = eeprom_read_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT); - scriptId = interface::moodLightController().nextScriptId(scriptId); eeprom_write_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT, scriptId); - interface::moodLightController().playScript(scriptId); + interface::popScreen(); break; case ButtonArray::ZERO: - scriptId = eeprom_read_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT); if ( scriptId == 1 ) { //Set RGB Values interface::pushScreen(&moodLightSetRGBScreen); } + break; + case ButtonArray::ZPLUS: + // increment more + for ( i = 0; i < 5; i ++ ) + scriptId = interface::moodLightController().nextScriptId(scriptId); + interface::moodLightController().playScript(scriptId); + break; + + case ButtonArray::ZMINUS: + // decrement more + for ( i = 0; i < 5; i ++ ) + scriptId = interface::moodLightController().prevScriptId(scriptId); + interface::moodLightController().playScript(scriptId); break; case ButtonArray::YPLUS: + // increment less + scriptId = interface::moodLightController().nextScriptId(scriptId); + interface::moodLightController().playScript(scriptId); + break; + case ButtonArray::YMINUS: + // decrement less + scriptId = interface::moodLightController().prevScriptId(scriptId); + interface::moodLightController().playScript(scriptId); + break; + case ButtonArray::XMINUS: case ButtonArray::XPLUS: - case ButtonArray::ZMINUS: - case ButtonArray::ZPLUS: - break; + break; case ButtonArray::CANCEL: + scriptId = eeprom_read_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT); + interface::moodLightController().playScript(scriptId); interface::popScreen(); break; } diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 2fb0b11..270b6ec 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -448,6 +448,8 @@ class MoodLightMode: public Screen { private: uint8_t updatePhase; + uint8_t scriptId; + MoodLightSetRGBScreen moodLightSetRGBScreen; public: diff --git a/firmware/src/shared/MoodLightController.cc b/firmware/src/shared/MoodLightController.cc index 7a8baea..c843c79 100755 --- a/firmware/src/shared/MoodLightController.cc +++ b/firmware/src/shared/MoodLightController.cc @@ -206,12 +206,23 @@ const ColorLookup clut PROGMEM = { uint8_t MoodLightController::nextScriptId(uint8_t currentScriptId) { if ( currentScriptId <= kDefinedColors ) return currentScriptId + 1; - if ( currentScriptId == (kDefinedColors + 1) ) return 100; + if ( currentScriptId == (kDefinedColors + 1) ) return kBlinkMStart; if (( currentScriptId >= kBlinkMStart ) && ( currentScriptId < kBlinkMEnd)) return currentScriptId + 1; return 0; } +//Given a scriptId, return the previous scriptId +//There has to be a better way, however PROGMEM precludes it + +uint8_t MoodLightController::prevScriptId(uint8_t currentScriptId) { + if (( currentScriptId <= (kDefinedColors + 1) ) && ( currentScriptId >= 1 )) return currentScriptId - 1; + if ( currentScriptId == kBlinkMStart ) return kDefinedColors + 1; + if (( currentScriptId > kBlinkMStart ) && ( currentScriptId <= kBlinkMEnd)) return currentScriptId - 1; + return kBlinkMEnd; +} + + void MoodLightController::playScript(uint8_t scriptId) { lastScriptPlayed = scriptId; diff --git a/firmware/src/shared/MoodLightController.hh b/firmware/src/shared/MoodLightController.hh index 0713e9a..0decdaf 100755 --- a/firmware/src/shared/MoodLightController.hh +++ b/firmware/src/shared/MoodLightController.hh @@ -43,6 +43,8 @@ public: //Used for Menu const PROGMEM prog_uchar *scriptIdToStr(uint8_t scriptId); uint8_t nextScriptId(uint8_t currentScriptId); + uint8_t prevScriptId(uint8_t currentScriptId); + bool start(); void playScript(uint8_t scriptId); From f7afa7dbf38ce5f8e610a46d38bf4740285332d2 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Mon, 30 Jan 2012 18:34:56 -0700 Subject: [PATCH 46/61] Added "Position" menu to show current X/Y/Z/A position --- firmware/src/shared/Menu.cc | 71 +++++++++++++++++++++++++++++++++---- firmware/src/shared/Menu.hh | 14 ++++++++ 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 4f52c64..3cb8cbd 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1756,7 +1756,7 @@ int64_t MainMenu::checkAndGetEepromDefault(const uint16_t location, const int64_ } MainMenu::MainMenu() { - itemCount = 20; + itemCount = 21; reset(); //Read in the axisStepsPerMM, we'll need these for various firmware functions later on @@ -1786,6 +1786,7 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar calibrate[] = "Calibrate"; const static PROGMEM prog_uchar homeOffsets[] = "Home Offsets"; const static PROGMEM prog_uchar filamentUsed[] = "Filament Used"; + const static PROGMEM prog_uchar currentPosition[]= "Position"; const static PROGMEM prog_uchar endStops[] = "Test End Stops"; const static PROGMEM prog_uchar stepsPerMm[] = "Axis Steps:mm"; const static PROGMEM prog_uchar versions[] = "Version"; @@ -1841,15 +1842,18 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(filamentUsed); break; case 16: - lcd.writeFromPgmspace(endStops); + lcd.writeFromPgmspace(currentPosition); break; case 17: - lcd.writeFromPgmspace(stepsPerMm); + lcd.writeFromPgmspace(endStops); break; case 18: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(stepsPerMm); break; case 19: + lcd.writeFromPgmspace(versions); + break; + case 20: lcd.writeFromPgmspace(snake); break; } @@ -1923,18 +1927,22 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&filamentUsedMode); break; case 16: + // Show Current Position Mode + interface::pushScreen(¤tPositionMode); + break; + case 17: // Show test end stops menu interface::pushScreen(&testEndStopsMode); break; - case 17: + case 18: // Show steps per mm menu interface::pushScreen(&stepsPerMMMode); break; - case 18: + case 19: // Show build from SD screen interface::pushScreen(&versionMode); break; - case 19: + case 20: // Show build from SD screen interface::pushScreen(&snake); break; @@ -4052,4 +4060,53 @@ void ProfileDisplaySettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { void ProfileDisplaySettingsMenu::handleSelect(uint8_t index) { } +void CurrentPositionMode::reset() { +} + +void CurrentPositionMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar msg1[] = "X:"; + const static PROGMEM prog_uchar msg2[] = "Y:"; + const static PROGMEM prog_uchar msg3[] = "Z:"; + const static PROGMEM prog_uchar msg4[] = "A:"; + const static PROGMEM prog_uchar mm[] = "mm"; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(msg1); + + lcd.setCursor(0,1); + lcd.writeFromPgmspace(msg2); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(msg3); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(msg4); + } + + Point position = steppers::getPosition(); + + lcd.setCursor(3, 0); + lcd.writeFloat(stepsToMM(position[0], AXIS_X), 3); + lcd.writeFromPgmspace(mm); + + lcd.setCursor(3, 1); + lcd.writeFloat(stepsToMM(position[1], AXIS_Y), 3); + lcd.writeFromPgmspace(mm); + + lcd.setCursor(3, 2); + lcd.writeFloat(stepsToMM(position[2], AXIS_Z), 3); + lcd.writeFromPgmspace(mm); + + lcd.setCursor(3, 3); + lcd.writeFloat(stepsToMM(position[3], AXIS_A), 3); + lcd.writeFromPgmspace(mm); +} + +void CurrentPositionMode::notifyButtonPressed(ButtonArray::ButtonName button) { + interface::popScreen(); +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 270b6ec..af3d7d5 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -756,6 +756,19 @@ protected: void handleSelect(uint8_t index); }; +class CurrentPositionMode: public Screen { +private: + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class MainMenu: public Menu { public: MainMenu(); @@ -783,6 +796,7 @@ private: HomeOffsetsMode homeOffsetsMode; StepsPerMMMode stepsPerMMMode; FilamentUsedMode filamentUsedMode; + CurrentPositionMode currentPositionMode; TestEndStopsMode testEndStopsMode; VersionMode versionMode; MoodLightMode moodLightMode; From 25be6ed7a160f1cacff5b1aee89c7690d6375d43 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Mon, 30 Jan 2012 19:02:54 -0700 Subject: [PATCH 47/61] Added warning screen when user selected a file from SD card that is >=31 chars in length --- firmware/src/shared/Menu.cc | 38 +++++++++++++++++++++++++++++++++++++ firmware/src/shared/Menu.hh | 11 +++++++++++ 2 files changed, 49 insertions(+) diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 3cb8cbd..fe33c4f 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -2107,6 +2107,7 @@ void SDMenu::handleSelect(uint8_t index) { e = host::startBuildFromSD(true); if (e != sdcard::SD_SUCCESS) { // TODO: report error + interface::pushScreen(&unableToOpenFileMenu); return; } } @@ -4109,4 +4110,41 @@ void CurrentPositionMode::notifyButtonPressed(ButtonArray::ButtonName button) { interface::popScreen(); } + //Unable to open file, filename too long? +UnableToOpenFileMenu::UnableToOpenFileMenu() { + itemCount = 4; + reset(); +} + +void UnableToOpenFileMenu::resetState() { + itemIndex = 3; + firstItemIndex = 3; +} + +void UnableToOpenFileMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg1[] = "Failed to open"; + const static PROGMEM prog_uchar msg2[] = "file. Name too"; + const static PROGMEM prog_uchar msg3[] = "long?"; + const static PROGMEM prog_uchar cont[] = "Continue"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg1); + break; + case 1: + lcd.writeFromPgmspace(msg2); + break; + case 2: + lcd.writeFromPgmspace(msg3); + break; + case 3: + lcd.writeFromPgmspace(cont); + break; + } +} + +void UnableToOpenFileMenu::handleSelect(uint8_t index) { + interface::popScreen(); +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index af3d7d5..39b5d37 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -172,12 +172,23 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class UnableToOpenFileMenu: public Menu { +public: + UnableToOpenFileMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; class SDMenu: public Menu { private: uint8_t updatePhase; uint8_t lastItemIndex; bool drawItemLockout; + UnableToOpenFileMenu unableToOpenFileMenu; public: SDMenu(); From 21032366d2a81038bc437b1e2b7c1204a3c765ee Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 17 Feb 2012 23:27:54 -0700 Subject: [PATCH 48/61] Added Marlin Acceleration. 100mm/s (and fatser) is achievable on a ToM. --- firmware/src/Motherboard/Command.cc | 32 +- firmware/src/Motherboard/EepromMap.cc | 19 + firmware/src/Motherboard/EepromMap.hh | 32 +- firmware/src/Motherboard/Main.cc | 1 + firmware/src/Motherboard/Steppers.cc | 391 ++++++++-- firmware/src/Motherboard/Steppers.hh | 15 + .../Motherboard/boards/mb24/Configuration.hh | 3 + .../Motherboard/boards/mb24/Motherboard.cc | 84 ++- .../Motherboard/boards/mb24/Motherboard.hh | 18 +- firmware/src/shared/CircularBuffer.hh | 10 + firmware/src/shared/Eeprom.cc | 29 + firmware/src/shared/Eeprom.hh | 4 +- firmware/src/shared/InterfaceBoard.hh | 2 +- firmware/src/shared/Menu.cc | 709 +++++++++++++++++- firmware/src/shared/Menu.hh | 114 +++ firmware/src/shared/StepperAccel.cc | 537 +++++++++++++ firmware/src/shared/StepperAccel.hh | 72 ++ firmware/src/shared/StepperAccelPlanner.cc | 650 ++++++++++++++++ firmware/src/shared/StepperAccelPlanner.hh | 144 ++++ firmware/src/shared/StepperAccelSpeedTable.hh | 77 ++ firmware/src/shared/StepperAxis.cc | 17 + firmware/src/shared/StepperAxis.hh | 10 +- 22 files changed, 2860 insertions(+), 110 deletions(-) create mode 100644 firmware/src/shared/StepperAccel.cc create mode 100644 firmware/src/shared/StepperAccel.hh create mode 100644 firmware/src/shared/StepperAccelPlanner.cc create mode 100644 firmware/src/shared/StepperAccelPlanner.hh create mode 100644 firmware/src/shared/StepperAccelSpeedTable.hh diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index 2e1e6b4..736bd59 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -29,6 +29,10 @@ #include "SDCard.hh" #include "ExtruderControl.hh" +#ifdef HAS_STEPPER_ACCELERATION +#include "StepperAccel.hh" +#endif + namespace command { #define COMMAND_BUFFER_SIZE 512 @@ -129,6 +133,7 @@ enum { Timeout delay_timeout; Timeout homing_timeout; Timeout tool_wait_timeout; +bool acceleration; void reset() { pauseAtZPos(0.0); @@ -140,6 +145,10 @@ void reset() { firstHeatTool0 = true; firstHeatHbp = true; mode = READY; + + uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + if ( accel & 0x01 ) acceleration = true; + else acceleration = false; } @@ -373,6 +382,27 @@ void runCommandSlice() { mode = READY; } } + +#ifdef HAS_STEPPER_ACCELERATION + //If we're running acceleration, we want to populate the pipeline buffer, + //but we also need to sync (wait for the pipeline buffer to clear) on certain + //commands, we do that here + if (( acceleration ) && ( mode == READY ) && ( ! estimating )) { + if (command_buffer.getLength() > 0) { + uint8_t command = command_buffer.peek(); + + //If we're not pipeline'able command, then we sync here, + //by waiting for the pipeline buffer to empty before continuing + if ((command != HOST_CMD_QUEUE_POINT_ABS) && + (command != HOST_CMD_QUEUE_POINT_EXT) && + (command != HOST_CMD_QUEUE_POINT_NEW)) { + if ( ! st_empty() ) return; + } + } + else return; + } +#endif + Point p; if (mode == READY) { // process next command on the queue. @@ -496,7 +526,6 @@ void runCommandSlice() { if ( ! estimating ) tool_wait_timeout.start(toolTimeout*1000000L); } } else if (command == HOST_CMD_WAIT_FOR_PLATFORM) { - // FIXME: Almost equivalent to WAIT_FOR_TOOL if (command_buffer.getLength() >= 6) { mode = WAIT_ON_PLATFORM; command_buffer.pop(); @@ -506,7 +535,6 @@ void runCommandSlice() { if ( ! estimating ) tool_wait_timeout.start(toolTimeout*1000000L); } } else if (command == HOST_CMD_STORE_HOME_POSITION) { - // check for completion if (command_buffer.getLength() >= 2) { command_buffer.pop(); diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index 0d7970b..4478987 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -51,6 +51,25 @@ void setDefaults() { eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,1); eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,0); eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,0); + eeprom_write_byte((uint8_t*)eeprom::STEPPER_DRIVER,0); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X,160); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y,160); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z,10); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A,100); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_B,100); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X,2000); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y,2000); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z,150); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A,60000); + putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM,5000); + putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT,3000); + putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,44); //Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,0); //Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,0); //Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_MAX_XY_JERK,2); //30mm/s Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_MAX_Z_JERK,100); //10mm/s Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_ADVANCE_K,50); //0.00001 Multiplied by 100000 + putEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER,175); //1.75 Multiplied by 100 } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index a5af0f8..c91458e 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -107,7 +107,37 @@ const static uint16_t OVERRIDE_GCODE_TEMP = 0x00C5; //4 Profiles = 0x00C6 + PROFILE_NEXT_OFFSET * 4 const static uint16_t PROFILE_BASE = 0x00C6; -//Next free location: 0x126 +//1 = Accelerated Stepper Driver, 0 = Regular stepper driver (default) +//Bit 2 is planner enabled +const static uint16_t STEPPER_DRIVER = 0x0126; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_MAX_FEEDRATE_X = 0x0127; +const static uint16_t ACCEL_MAX_FEEDRATE_Y = 0x012B; +const static uint16_t ACCEL_MAX_FEEDRATE_Z = 0x012F; +const static uint16_t ACCEL_MAX_FEEDRATE_A = 0x0133; +const static uint16_t ACCEL_MAX_FEEDRATE_B = 0x0137; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_MAX_ACCELERATION_X = 0x013B; +const static uint16_t ACCEL_MAX_ACCELERATION_Y = 0x013F; +const static uint16_t ACCEL_MAX_ACCELERATION_Z = 0x0143; +const static uint16_t ACCEL_MAX_ACCELERATION_A = 0x0147; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_MAX_EXTRUDER_NORM = 0x014B; +const static uint16_t ACCEL_MAX_EXTRUDER_RETRACT= 0x014F; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_E_STEPS_PER_MM = 0x0153; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_MIN_FEED_RATE = 0x0157; +const static uint16_t ACCEL_MIN_TRAVEL_FEED_RATE= 0x015B; +const static uint16_t ACCEL_MAX_XY_JERK = 0x015F; +const static uint16_t ACCEL_MAX_Z_JERK = 0x0163; +const static uint16_t ACCEL_ADVANCE_K = 0x0167; +const static uint16_t ACCEL_FILAMENT_DIAMETER = 0x016B; /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/Motherboard/Main.cc b/firmware/src/Motherboard/Main.cc index 4bd6485..78e86bc 100644 --- a/firmware/src/Motherboard/Main.cc +++ b/firmware/src/Motherboard/Main.cc @@ -43,6 +43,7 @@ void reset(bool hard_reset) { Motherboard& board = Motherboard::getBoard(); sdcard::reset(); steppers::abort(); + steppers::reset(); command::reset(); eeprom::init(); board.reset(hard_reset); diff --git a/firmware/src/Motherboard/Steppers.cc b/firmware/src/Motherboard/Steppers.cc index f6212d4..a3155c5 100644 --- a/firmware/src/Motherboard/Steppers.cc +++ b/firmware/src/Motherboard/Steppers.cc @@ -19,6 +19,15 @@ #include "Steppers.hh" #include "StepperAxis.hh" #include +#include +#include +#include +#include "EepromMap.hh" +#include "Eeprom.hh" + +#ifdef HAS_STEPPER_ACCELERATION +#include "StepperAccel.hh" +#endif namespace steppers { @@ -29,6 +38,15 @@ volatile int32_t intervals_remaining; StepperAxis axes[STEPPER_COUNT]; volatile bool is_homing; +#ifdef HAS_STEPPER_ACCELERATION + bool acceleration = false; + bool planner = false; + uint8_t plannerMaxBufferSize; + volatile bool force_acceleration_off = false; + + Point lastTarget; +#endif + bool holdZ = false; bool isRunning() { @@ -45,26 +63,140 @@ void init(Motherboard& motherboard) { for (int i = 0; i < STEPPER_COUNT; i++) { axes[i] = StepperAxis(motherboard.getStepperInterface(i)); } + + reset(); } void abort() { +#ifdef HAS_STEPPER_ACCELERATION + if ( acceleration ) quickStop(); +#endif is_running = false; is_homing = false; } +float convertAxisMMToFloat(int64_t st ) { + float aspmf = (float)st; + for (uint8_t i=0 ; i < STEPS_PER_MM_PRECISION; i ++ ) + aspmf /= 10.0; + return aspmf; +} + +void reset() { +#ifdef HAS_STEPPER_ACCELERATION + //Get the acceleration settings + uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + + acceleration = accel & 0x01; + planner = (accel & 0x02)?true:false; + + if ( acceleration ) { + //Good description of the settings can be found here: + //http://wiki.ultimaker.com/Marlin_firmware_for_the_Ultimaker + //and here: https://github.com/ErikZalm/Marlin + + //Here's more documentation on the various settings / features + //http://wiki.ultimaker.com/Marlin_firmware_for_the_Ultimaker + //https://github.com/ErikZalm/Marlin/commits/Marlin_v1 + //http://forums.reprap.org/read.php?147,94689,94689 + //http://reprap.org/pipermail/reprap-dev/2011-May/003323.html + //http://www.brokentoaster.com/blog/?p=358 + + //Same as axis steps:mm in the firmware + //Temporarily placed here, should be read from eeprom + axis_steps_per_unit[X_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_X, STEPS_PER_MM_X_DEFAULT)); + axis_steps_per_unit[Y_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Y, STEPS_PER_MM_Y_DEFAULT)); + axis_steps_per_unit[Z_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Z, STEPS_PER_MM_Z_DEFAULT)); + axis_steps_per_unit[E_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,44) / 10.0; + + //M201 - Set max acceleration in units/s^2 for print moves + // X, Y, Z, E maximum start speed for accelerated moves. E default values are good for skeinforge 40+, for older versions raise them a lot. + max_acceleration_units_per_sq_second[X_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, 2000); + max_acceleration_units_per_sq_second[Y_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, 2000); + max_acceleration_units_per_sq_second[Z_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, 150); + max_acceleration_units_per_sq_second[E_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, 60000); + + for (uint8_t i = 0; i < NUM_AXIS; i ++) + axis_steps_per_sqr_second[i] = max_acceleration_units_per_sq_second[i] * axis_steps_per_unit[i]; + + //M203 - Set maximum feedrate that your machine can sustain in mm/sec + max_feedrate[X_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, 160); + max_feedrate[Y_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, 160); + max_feedrate[Z_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, 10); + max_feedrate[E_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, 100); + + //M204 - Set default accelerationm for "Normal Moves (acceleration)" and "filament only moves (retraction)" in mm/sec^2 + + // X, Y, Z and E max acceleration in mm/s^2 for printing moves + p_acceleration = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, 5000); + + // X, Y, Z and E max acceleration in mm/s^2 for r retracts + p_retract_acceleration = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, 3000); + + //M205 - Advanced Settings + //minimumfeedrate - minimum travel speed while printing + minimumfeedrate = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,0) / 10.0; + + //mintravelfeedrate - minimum travel speed while travelling + mintravelfeedrate = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,0) / 10.0; + + //max_xy_jerk - maximum xy jerk (mm/sec) + max_xy_jerk = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_XY_JERK,2) / 10.0; + max_xy_jerk_squared = max_xy_jerk * max_xy_jerk; + + //max_z_jerk - maximum z jerk (mm/sec) + max_z_jerk = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_Z_JERK,100) / 10.0; + + float advanceK = (float)eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K,50) / 100000.0; + float filamentDiameter = (float)eeprom::getEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER,175) / 100.0; + + force_acceleration_off = false; + + if ( planner ) plannerMaxBufferSize = BLOCK_BUFFER_SIZE - 1; + else plannerMaxBufferSize = 1; + + plan_init(advanceK, filamentDiameter, axis_steps_per_unit[E_AXIS]); //Initialize planner + st_init(); //Initialize stepper + + lastTarget = Point(st_get_position(X_AXIS), st_get_position(Y_AXIS), st_get_position(Z_AXIS), st_get_position(E_AXIS), 0); + } + else lastTarget = getPosition(); +#endif +} + /// Define current position as given point void definePosition(const Point& position) { - for (int i = 0; i < STEPPER_COUNT; i++) { - axes[i].definePosition(position[i]); +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + plan_set_position(position[0], position[1], position[2], position[3]); } + else { +#endif + for (int i = 0; i < STEPPER_COUNT; i++) { + axes[i].definePosition(position[i]); + } +#ifdef HAS_STEPPER_ACCELERATION + } + lastTarget = position; +#endif } /// Get current position const Point getPosition() { #if STEPPER_COUNT > 3 - return Point(axes[0].position,axes[1].position,axes[2].position,axes[3].position,axes[4].position); +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) + return lastTarget; + else +#endif + return Point(axes[0].position,axes[1].position,axes[2].position,axes[3].position,axes[4].position); #else - return Point(axes[0].position,axes[1].position,axes[2].position); +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) + return Point(lastTarget[0], lastTarget[1], lastTarget[2]); + else +#endif + return Point(axes[0].position,axes[1].position,axes[2].position); #endif } @@ -72,51 +204,175 @@ void setHoldZ(bool holdZ_in) { holdZ = holdZ_in; } +#ifdef HAS_STEPPER_ACCELERATION + +//Calculates the feedrate based on moving from "from" to "to" + +float calcFeedRate(const Point& from, const Point& to, int32_t interval ) { + + //Calculate the distance in mm's by: + //Calculate the delta distances and convert to mm's + //Then sqr of sum of squares of the deltas for the distance + + //We also calculate at the same time, master_steps in steps by finding the dominant axis (for all 5) + //You would think it would be for X/Y/Z only, but they "mistakenly" use all 5 in rep g. + //so we do the same here for consistancy + + int32_t master_steps = 0; + float distance = 0.0; + + for (uint8_t i = 0; i < AXIS_COUNT; i ++ ) { + int32_t delta = to[i] - from[i]; + if ( delta < 0 ) delta *= -1; + + if ( delta > master_steps ) master_steps = delta; + + const float delta_mm = (float)delta / axis_steps_per_unit[i]; + distance += delta_mm * delta_mm; + } + distance = sqrt(distance); + + return (distance * 60000000.0) / ((float)interval * (float)master_steps); +} + +#endif + void setTarget(const Point& target, int32_t dda_interval) { - int32_t max_delta = 0; - for (int i = 0; i < AXIS_COUNT; i++) { - axes[i].setTarget(target[i], false); - const int32_t delta = axes[i].delta; - // Only shut z axis on inactivity - if (i == 2 && !holdZ) axes[i].enableStepper(delta != 0); - else if (delta != 0) axes[i].enableStepper(true); - if (delta > max_delta) { - max_delta = delta; +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + float feedRate = calcFeedRate(lastTarget, target, dda_interval ); + + //Figure out the distance between x1,y1 and x2,y2 using pythagoras + plan_buffer_line(target[0], target[1], target[2], target[3], feedRate, 0); + } else { +#endif + int32_t max_delta = 0; + for (int i = 0; i < AXIS_COUNT; i++) { + axes[i].setTarget(target[i], false); + const int32_t delta = axes[i].delta; + // Only shut z axis on inactivity + if (i == 2 && !holdZ) axes[i].enableStepper(delta != 0); + else if (delta != 0) axes[i].enableStepper(true); + if (delta > max_delta) { + max_delta = delta; + } } + // compute number of intervals for this move + intervals = ((max_delta * dda_interval) / INTERVAL_IN_MICROSECONDS); + intervals_remaining = intervals; + const int32_t negative_half_interval = -intervals / 2; + for (int i = 0; i < AXIS_COUNT; i++) { + axes[i].counter = negative_half_interval; + } + is_running = true; + +#ifdef HAS_STEPPER_ACCELERATION } - // compute number of intervals for this move - intervals = ((max_delta * dda_interval) / INTERVAL_IN_MICROSECONDS); - intervals_remaining = intervals; - const int32_t negative_half_interval = -intervals / 2; - for (int i = 0; i < AXIS_COUNT; i++) { - axes[i].counter = negative_half_interval; - } - is_running = true; + + lastTarget = target; +#endif } void setTargetNew(const Point& target, int32_t us, uint8_t relative) { - for (int i = 0; i < AXIS_COUNT; i++) { - axes[i].setTarget(target[i], (relative & (1 << i)) != 0); - // Only shut z axis on inactivity - const int32_t delta = axes[i].delta; - if (i == 2 && !holdZ) { - axes[i].enableStepper(delta != 0); - } else if (delta != 0) { - axes[i].enableStepper(true); +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + Point newPosition = target; + for (uint8_t i = 0; i < AXIS_COUNT; i ++) { + if ((relative & (1 << i)) != 0) + newPosition[i] = lastTarget[i] + target[i]; + } + + int32_t max_delta = 0; + for (int i = 0; i < AXIS_COUNT; i++) { + int32_t delta = newPosition[i] - lastTarget[i]; + if ( delta < 0 ) delta *= -1; + if (delta > max_delta) { + max_delta = delta; + } + } + int32_t dda_interval = us / max_delta; + float feedRate = calcFeedRate(lastTarget, newPosition, dda_interval); + + plan_buffer_line(newPosition[0], newPosition[1], newPosition[2], newPosition[3], feedRate, 0); + lastTarget = newPosition; + } else { +#endif + for (int i = 0; i < AXIS_COUNT; i++) { + axes[i].setTarget(target[i], (relative & (1 << i)) != 0); + // Only shut z axis on inactivity + const int32_t delta = axes[i].delta; + if (i == 2 && !holdZ) { + axes[i].enableStepper(delta != 0); + } else if (delta != 0) { + axes[i].enableStepper(true); + } + +#ifdef HAS_STEPPER_ACCELERATION + lastTarget[i] = axes[i].absoluteTarget; +#endif + } + // compute number of intervals for this move + intervals = us / INTERVAL_IN_MICROSECONDS; + intervals_remaining = intervals; + const int32_t negative_half_interval = -intervals / 2; + for (int i = 0; i < AXIS_COUNT; i++) { + axes[i].counter = negative_half_interval; } + is_running = true; + +#ifdef HAS_STEPPER_ACCELERATION } - // compute number of intervals for this move - intervals = us / INTERVAL_IN_MICROSECONDS; - intervals_remaining = intervals; - const int32_t negative_half_interval = -intervals / 2; - for (int i = 0; i < AXIS_COUNT; i++) { - axes[i].counter = negative_half_interval; +#endif +} + +#ifdef HAS_STEPPER_ACCELERATION +void switchToRegularDriver() { + if ( ! acceleration ) return; + + cli(); + + //Get the current position of the accelerated driver and + //store it in the regular driver + Point currentPosition = getPosition(); + for (int i = 0; i < STEPPER_COUNT; i++) { + axes[i].definePosition(currentPosition[i]); } - is_running = true; + + force_acceleration_off = true; + + //Change the interrupt frequency to the regular driver + Motherboard::getBoard().setupFixedStepperTimer(); + + sei(); } +void switchToAcceleratedDriver() { + if ( ! acceleration ) return; + + //Get the current position of the regular driver and + //store it in the accelerated driver + + cli(); + + force_acceleration_off = false; + + Point currentPosition = Point(axes[0].position, axes[1].position, axes[2].position, axes[3].position, axes[4].position); + definePosition(currentPosition); + + //Change the interrupt frequency to the accelerated driver + Motherboard::getBoard().setupAccelStepperTimer(); + + sei(); +} +#endif + /// Start homing void startHoming(const bool maximums, const uint8_t axes_enabled, const uint32_t us_per_step) { + +#ifdef HAS_STEPPER_ACCELERATION + if ( acceleration ) switchToRegularDriver(); +#endif + intervals_remaining = INT32_MAX; intervals = us_per_step / INTERVAL_IN_MICROSECONDS; const int32_t negative_half_interval = -intervals / 2; @@ -160,26 +416,63 @@ bool isAtMinimum(uint8_t index) { return false; } +void doLcd() { +// Motherboard::getBoard().lcd.setCursor(0,3); +// Motherboard::getBoard().lcd.writeFloat((float)movesplanned(),1); +// Motherboard::getBoard().lcd.write(' '); +} bool doInterrupt() { - if (is_running) { - if (intervals_remaining-- == 0) { - is_running = false; - } else { +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + //st_interrupt runs all the time, is_running reflects if we have space in the buffer or not, + //i.e. it enables us to add more commands if it's false + + if ( movesplanned() >= plannerMaxBufferSize) is_running = true; + else is_running = false; + + st_interrupt(); + + return is_running; + } else { +#endif + if (is_running) { + if (intervals_remaining-- == 0) { + is_running = false; + } else { + for (int i = 0; i < STEPPER_COUNT; i++) { + axes[i].doInterrupt(intervals); + } + } + return is_running; + } else if (is_homing) { + is_homing = false; for (int i = 0; i < STEPPER_COUNT; i++) { - axes[i].doInterrupt(intervals); + bool still_homing = axes[i].doHoming(intervals); + is_homing = still_homing || is_homing; } + +#ifdef HAS_STEPPER_ACCELERATION + //If homing has finished and we're accelerated, switch back to the accelerated drived + if (( ! is_homing ) && ( acceleration )) switchToAcceleratedDriver(); +#endif + + return is_homing; } - return is_running; - } else if (is_homing) { - is_homing = false; - for (int i = 0; i < STEPPER_COUNT; i++) { - bool still_homing = axes[i].doHoming(intervals); - is_homing = still_homing || is_homing; - } - return is_homing; +#ifdef HAS_STEPPER_ACCELERATION } +#endif return false; } +#ifdef HAS_STEPPER_ACCELERATION + +bool doAdvanceInterrupt() { +#ifdef ADVANCE + if ( acceleration ) st_advance_interrupt(); +#endif +} + +#endif + } diff --git a/firmware/src/Motherboard/Steppers.hh b/firmware/src/Motherboard/Steppers.hh index e8ede59..d6c3b1d 100644 --- a/firmware/src/Motherboard/Steppers.hh +++ b/firmware/src/Motherboard/Steppers.hh @@ -33,6 +33,9 @@ namespace steppers { /// \param[in] motherboard Motherboard to attach the steppers to. void init(Motherboard& motherboard); + //Reset the stepper subsystem + void reset(); + /// Check if the stepper subsystem is running /// \return True if the stepper subsystem is running or paused. False /// otherwise. @@ -84,10 +87,22 @@ namespace steppers { /// \param[in] position New system position void definePosition(const Point& position); + /// Switch to the regular driver + void switchToRegularDriver(); + + ///Switch to the accelerated driver + void switchToAcceleratedDriver(); + +//Debugging +void doLcd(); + /// Handle interrupt. /// \return True if the stepper subsystem is currently in motion. bool doInterrupt(); + //Used for the ADVANCE algorithm in Marlin + bool doAdvanceInterrupt(); + /// Get the current system position /// \return The current machine position. const Point getPosition(); diff --git a/firmware/src/Motherboard/boards/mb24/Configuration.hh b/firmware/src/Motherboard/boards/mb24/Configuration.hh index 2588aad..ced7ede 100644 --- a/firmware/src/Motherboard/boards/mb24/Configuration.hh +++ b/firmware/src/Motherboard/boards/mb24/Configuration.hh @@ -193,4 +193,7 @@ #define HAS_ATX_POWER_GOOD 1 #define ATX_POWER_GOOD Pin(PortK,2) //Pin ATX 8 connected to Analog 10 +//Stepper Acceleration +#define HAS_STEPPER_ACCELERATION 1 + #endif // BOARDS_RRMBV12_CONFIGURATION_HH_ diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index 33138dd..f991ada 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -94,6 +94,31 @@ Motherboard::Motherboard() : #endif } +void Motherboard::setupFixedStepperTimer() { + TCCR1A = 0x00; + TCCR1B = 0x09; + TCCR1C = 0x00; + OCR1A = INTERVAL_IN_MICROSECONDS * 16; + TIMSK1 = 0x02; // turn on OCR1A match interrupt +} + +void Motherboard::setupAccelStepperTimer() { + // waveform generation = 0100 = CTC + TCCR1B &= ~(1< 1000000L) { seconds += 1; countupMicros -= 1000000L; } - - steppers::doInterrupt(); } void Motherboard::runMotherboardSlice() { @@ -233,7 +281,17 @@ MoodLightController Motherboard::getMoodLightController() { /// Timer one comparator match interrupt ISR(TIMER1_COMPA_vect) { - Motherboard::getBoard().doInterrupt(); + Motherboard::getBoard().doStepperInterrupt(); +} + +/// Timer one comparator match interrupt +ISR(TIMER3_COMPA_vect) { + Motherboard::getBoard().doInterfaceInterrupt(); +} + +/// Timer one comparator match interrupt +ISR(TIMER4_COMPA_vect) { + Motherboard::getBoard().doAdvanceInterrupt(); } /// Number of times to blink the debug LED on each cycle diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.hh b/firmware/src/Motherboard/boards/mb24/Motherboard.hh index 459422b..8f48c4a 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.hh +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.hh @@ -95,6 +95,10 @@ private: void serviceBuzzer(); public: + //2 types of stepper timers depending on if we're using accelerated or not + void setupFixedStepperTimer(); + void setupAccelStepperTimer(); + /// Reset the motherboard to its initial state. /// This only resets the board, and does not send a reset /// to any attached toolheads. @@ -110,6 +114,11 @@ public: return stepper[n]; } + StepperInterface *getStepperAllInterfaces() + { + return stepper; + } + /// Get the number of microseconds that have passed since /// the board was initialized. This value will wrap after /// 2**32 microseconds (ca. 70 minutes); callers should compensate for this. @@ -122,8 +131,13 @@ public: /// Get the current error being displayed. uint8_t getCurrentError(); - /// Perform the timer interrupt routine. - void doInterrupt(); + /// Perform the stepper timer interrupt routine. + void doStepperInterrupt(); + + void doAdvanceInterrupt(); + + /// Perform the interface timer interrupt routine. + void doInterfaceInterrupt(); MoodLightController getMoodLightController(); void MoodLightSetRGBColor(uint8_t r, uint8_t g, uint8_t b, uint8_t fadeSpeed, uint8_t writeToEeprom); diff --git a/firmware/src/shared/CircularBuffer.hh b/firmware/src/shared/CircularBuffer.hh index a27cd7b..e1009cb 100644 --- a/firmware/src/shared/CircularBuffer.hh +++ b/firmware/src/shared/CircularBuffer.hh @@ -74,6 +74,16 @@ public: return popped_byte; } + /// Peek at a byte at the head of the buffer + inline BufDataType peek() { + if (isEmpty()) { + underflow = true; + return BufDataType(); + } + const BufDataType& popped_byte = operator[](0); + return popped_byte; + } + /// Pop a number of bytes off the head of the buffer. If there /// are not enough bytes to complete the pop, pop what we can and /// set the underflow flag. diff --git a/firmware/src/shared/Eeprom.cc b/firmware/src/shared/Eeprom.cc index ba6b7ca..cbb4a65 100644 --- a/firmware/src/shared/Eeprom.cc +++ b/firmware/src/shared/Eeprom.cc @@ -51,10 +51,39 @@ int64_t getEepromInt64(const uint16_t location, const int64_t default_value) { return *ret; } +uint32_t getEepromUInt32(const uint16_t location, const uint32_t default_value) { + uint32_t *ret; + uint8_t data[4]; + eeprom_read_block(data,(const uint8_t*)location,4); + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff && data[3] == 0xff) + return default_value; + ret = (uint32_t *)&data[0]; + return *ret; +} + void putEepromInt64(const uint16_t location, const int64_t value) { void *data; data = (void *)&value; eeprom_write_block(data,(void*)location,8); } +void putEepromUInt32(const uint16_t location, const uint32_t value) { + void *data; + data = (void *)&value; + eeprom_write_block(data,(void*)location,4); +} + +int64_t getEepromStepsPerMM(const uint16_t location, const int64_t default_value) { + int64_t value = eeprom::getEepromInt64(location, default_value); + + if (( value <= STEPS_PER_MM_LOWER_LIMIT ) || ( value >= STEPS_PER_MM_UPPER_LIMIT )) { + eeprom::putEepromInt64(location, default_value); + + //Just to be on the safe side + value = eeprom::getEepromInt64(location, default_value); + } + + return value; +} + } // namespace eeprom diff --git a/firmware/src/shared/Eeprom.hh b/firmware/src/shared/Eeprom.hh index 668ef2e..c3c54cb 100644 --- a/firmware/src/shared/Eeprom.hh +++ b/firmware/src/shared/Eeprom.hh @@ -11,8 +11,10 @@ uint8_t getEeprom8(const uint16_t location, const uint8_t default_value); uint16_t getEeprom16(const uint16_t location, const uint16_t default_value); float getEepromFixed16(const uint16_t location, const float default_value); int64_t getEepromInt64(const uint16_t location, const int64_t default_value); +uint32_t getEepromUInt32(const uint16_t location, const uint32_t default_value); void putEepromInt64(const uint16_t location, const int64_t value); - +void putEepromUInt32(const uint16_t location, const uint32_t value); +int64_t getEepromStepsPerMM(const uint16_t location, const int64_t default_value); } #define STEPS_PER_MM_PADDING 5 diff --git a/firmware/src/shared/InterfaceBoard.hh b/firmware/src/shared/InterfaceBoard.hh index eefa9b1..86d4adc 100644 --- a/firmware/src/shared/InterfaceBoard.hh +++ b/firmware/src/shared/InterfaceBoard.hh @@ -26,7 +26,7 @@ #include "MoodLightController.hh" /// Maximum number of screens that can be active at once. -#define SCREEN_STACK_DEPTH 5 +#define SCREEN_STACK_DEPTH 7 /// Character LCD screen geometry /// diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index fe33c4f..6df37ad 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -50,6 +50,61 @@ enum Axis { }; +//Stack checking +//http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=52249 +extern uint8_t _end; +extern uint8_t __stack; + +#define STACK_CANARY 0xc5 + +void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1"))); + +void StackPaint(void) +{ +#if 0 + uint8_t *p = &_end; + + while(p <= &__stack) + { + *p = STACK_CANARY; + p++; + } +#else + __asm volatile (" ldi r30,lo8(_end)\n" + " ldi r31,hi8(_end)\n" + " ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */ + " ldi r25,hi8(__stack)\n" + " rjmp .cmp\n" + ".loop:\n" + " st Z+,r24\n" + ".cmp:\n" + " cpi r30,lo8(__stack)\n" + " cpc r31,r25\n" + " brlo .loop\n" + " breq .loop"::); +#endif +} + + +uint16_t StackCount(void) +{ + const uint8_t *p = &_end; + uint16_t c = 0; + + while(*p == STACK_CANARY && p <= &__stack) + { + p++; + c++; + } + + return c; +} + + +void VersionMode::reset() { +} + + //Convert mm's to steps for the given axis //Accurate to 1/1000 mm @@ -236,10 +291,10 @@ int appendUint8(char *buf, uint8_t buflen, uint8_t val) void SplashScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar splash1[] = " "; - const static PROGMEM prog_uchar splash2[] = " Thing-O-Matic "; - const static PROGMEM prog_uchar splash3[] = " --------- "; - const static PROGMEM prog_uchar splash4[] = " "; + const static PROGMEM prog_uchar splash1[] = " Thing-O-Matic "; + const static PROGMEM prog_uchar splash2[] = " --------- "; + const static PROGMEM prog_uchar splash3[] = " Jetty Firmware "; + const static PROGMEM prog_uchar splash4[] = "Thing 15380 3.0b"; if (forceRedraw) { @@ -330,6 +385,8 @@ void JogMode::reset() { userViewMode = jogModeSettings & 0x01; userViewModeChanged = false; + + steppers::switchToRegularDriver(); } void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { @@ -483,6 +540,7 @@ void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { steppers::enableAxis(1, false); steppers::enableAxis(2, false); interface::popScreen(); + steppers::switchToAcceleratedDriver(); break; } } @@ -493,6 +551,7 @@ void ExtruderMode::reset() { timeChanged = false; lastDirection = 1; overrideExtrudeSeconds = 0; + steppers::switchToRegularDriver(); } void ExtruderMode::update(LiquidCrystal& lcd, bool forceRedraw) { @@ -666,6 +725,7 @@ void ExtruderMode::notifyButtonPressed(ButtonArray::ButtonName button) { steppers::abort(); steppers::enableAxis(3, false); interface::popScreen(); + steppers::switchToAcceleratedDriver(); break; } } @@ -1438,6 +1498,8 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { updatePhase = (enum UpdatePhase)((uint8_t)updatePhase + 1); if (updatePhase >= UPDATE_PHASE_LAST) updatePhase = UPDATE_PHASE_FIRST; + + steppers::doLcd(); } void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { @@ -1470,14 +1532,12 @@ void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } -void VersionMode::reset() { -} + void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar version1[] = "Firmware Version"; - const static PROGMEM prog_uchar version2[] = "----------------"; - const static PROGMEM prog_uchar version3[] = "Motherboard: _._"; - const static PROGMEM prog_uchar version4[] = " Extruder: _._"; + const static PROGMEM prog_uchar version1[] = "Motherboard: _._"; + const static PROGMEM prog_uchar version2[] = " Extruder: _._"; + const static PROGMEM prog_uchar version4[] = "FreeSram: "; if (forceRedraw) { lcd.clear(); @@ -1488,17 +1548,11 @@ void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,1); lcd.writeFromPgmspace(version2); - lcd.setCursor(0,2); - lcd.writeFromPgmspace(version3); - - lcd.setCursor(0,3); - lcd.writeFromPgmspace(version4); - //Display the motherboard version - lcd.setCursor(13, 2); + lcd.setCursor(13, 0); lcd.writeInt(firmware_version / 100, 1); - lcd.setCursor(15, 2); + lcd.setCursor(15, 0); lcd.writeInt(firmware_version % 100, 1); //Display the extruder version @@ -1507,15 +1561,19 @@ void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { if (extruderControl(SLAVE_CMD_VERSION, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t extruderVersion = responsePacket.read16(1); - lcd.setCursor(13, 3); + lcd.setCursor(13, 1); lcd.writeInt(extruderVersion / 100, 1); - lcd.setCursor(15, 3); + lcd.setCursor(15, 1); lcd.writeInt(extruderVersion % 100, 1); } else { - lcd.setCursor(13, 3); + lcd.setCursor(13, 1); lcd.writeString("X.X"); } + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(version4); + lcd.writeFloat((float)StackCount(),0); } else { } } @@ -1742,30 +1800,17 @@ void CancelBuildMenu::handleSelect(uint8_t index) { lind ++; } -int64_t MainMenu::checkAndGetEepromDefault(const uint16_t location, const int64_t default_value) { - int64_t value = eeprom::getEepromInt64(location, default_value); - - if (( value <= STEPS_PER_MM_LOWER_LIMIT ) || ( value >= STEPS_PER_MM_UPPER_LIMIT )) { - eeprom::putEepromInt64(location, default_value); - - //Just to be on the safe side - value = eeprom::getEepromInt64(location, default_value); - } - - return value; -} - MainMenu::MainMenu() { itemCount = 21; reset(); //Read in the axisStepsPerMM, we'll need these for various firmware functions later on cli(); - axisStepsPerMM[AXIS_X] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_X, STEPS_PER_MM_X_DEFAULT); - axisStepsPerMM[AXIS_Y] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_Y, STEPS_PER_MM_Y_DEFAULT); - axisStepsPerMM[AXIS_Z] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_Z, STEPS_PER_MM_Z_DEFAULT); - axisStepsPerMM[AXIS_A] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_A, STEPS_PER_MM_A_DEFAULT); - axisStepsPerMM[AXIS_B] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_B, STEPS_PER_MM_B_DEFAULT); + axisStepsPerMM[AXIS_X] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_X, STEPS_PER_MM_X_DEFAULT); + axisStepsPerMM[AXIS_Y] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Y, STEPS_PER_MM_Y_DEFAULT); + axisStepsPerMM[AXIS_Z] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Z, STEPS_PER_MM_Z_DEFAULT); + axisStepsPerMM[AXIS_A] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_A, STEPS_PER_MM_A_DEFAULT); + axisStepsPerMM[AXIS_B] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_B, STEPS_PER_MM_B_DEFAULT); sei(); } @@ -2599,6 +2644,8 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { case 0: //Entered pause, waiting for steppers to finish last command lcd.writeFromPgmspace(waitForCurrentCommand); + steppers::switchToRegularDriver(); + if ( ! steppers::isRunning()) pauseState ++; break; @@ -2704,6 +2751,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { interface::popScreen(); command::pause(false); if ( ! autoPause ) interface::popScreen(); + steppers::switchToAcceleratedDriver(); } break; } @@ -3490,7 +3538,7 @@ void FilamentUsedMode::notifyButtonPressed(ButtonArray::ButtonName button) { } BuildSettingsMenu::BuildSettingsMenu() { - itemCount = 3; + itemCount = 4; reset(); } @@ -3503,6 +3551,7 @@ void BuildSettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar item1[] = "EstimatePreheat"; const static PROGMEM prog_uchar item2[] = "Override Temp"; const static PROGMEM prog_uchar item3[] = "ABP Copies (SD)"; + const static PROGMEM prog_uchar item4[] = "Acceleration"; switch (index) { case 0: @@ -3514,6 +3563,9 @@ void BuildSettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { case 2: lcd.writeFromPgmspace(item3); break; + case 3: + lcd.writeFromPgmspace(item4); + break; } } @@ -3533,6 +3585,10 @@ void BuildSettingsMenu::handleSelect(uint8_t index) { //Change number of ABP copies interface::pushScreen(&abpCopiesSetScreen); break; + case 3: + //Acceleration menu + interface::pushScreen(&accelerationMenu); + break; } } @@ -3698,6 +3754,74 @@ void OverrideGCodeTempMenu::handleSelect(uint8_t index) { } } +StepperDriverAcceleratedMenu::StepperDriverAcceleratedMenu() { + itemCount = 5; + reset(); +} + +void StepperDriverAcceleratedMenu::resetState() { + uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + if ( accel == 0x03 ) itemIndex = 4; + else if ( accel == 0x01 ) itemIndex = 3; + else itemIndex = 2; + firstItemIndex = 2; +} + +void StepperDriverAcceleratedMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg1[] = "Accelerated"; + const static PROGMEM prog_uchar msg2[] = "Stepper Driver:"; + const static PROGMEM prog_uchar off[] = "Off"; + const static PROGMEM prog_uchar on[] = "On - No Planner"; + const static PROGMEM prog_uchar planner[]= "On - Planner"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg1); + break; + case 1: + lcd.writeFromPgmspace(msg2); + break; + case 2: + lcd.writeFromPgmspace(off); + break; + case 3: + lcd.writeFromPgmspace(on); + break; + case 4: + lcd.writeFromPgmspace(planner); + break; + } +} + +void StepperDriverAcceleratedMenu::handleSelect(uint8_t index) { + uint8_t oldValue = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + uint8_t newValue = oldValue; + + switch (index) { + case 2: + newValue = 0x00; + interface::popScreen(); + break; + case 3: + newValue = 0x01; + interface::popScreen(); + break; + case 4: + newValue = 0x03; + interface::popScreen(); + break; + } + + //If the value has changed, do a reset + if ( newValue != oldValue ) { + cli(); + eeprom_write_byte((uint8_t*)eeprom::STEPPER_DRIVER, newValue); + sei(); + //Reset + host::stopBuild(); + } +} + #define NUM_PROFILES 4 #define PROFILES_SAVED_AXIS 3 @@ -4147,4 +4271,509 @@ void UnableToOpenFileMenu::handleSelect(uint8_t index) { interface::popScreen(); } +void AcceleratedSettingsMode::reset() { + cli(); + values[0] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, 160); + values[1] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, 160); + values[2] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, 10); + values[3] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, 100); + values[4] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, 2000); + values[5] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, 2000); + values[6] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, 150); + values[7] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, 60000); + values[8] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, 5000); + values[9] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, 3000); + values[10] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,0); + values[11] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,0); + values[12] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_XY_JERK,2); + values[13] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_Z_JERK,100); + values[14] = eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K,50); + values[15] = eeprom::getEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER,175); + sei(); + + lastAccelerateSettingsState= AS_NONE; + accelerateSettingsState= AS_MAX_FEEDRATE_X; +} + +void AcceleratedSettingsMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1xMaxFeedRate[] = "X MaxFeedRate:"; + const static PROGMEM prog_uchar message1yMaxFeedRate[] = "Y MaxFeedRate:"; + const static PROGMEM prog_uchar message1zMaxFeedRate[] = "Z MaxFeedRate:"; + const static PROGMEM prog_uchar message1aMaxFeedRate[] = "A MaxFeedRate:"; + const static PROGMEM prog_uchar message1xMaxAccelRate[] = "X Max Accel:"; + const static PROGMEM prog_uchar message1yMaxAccelRate[] = "Y Max Accel:"; + const static PROGMEM prog_uchar message1zMaxAccelRate[] = "Z Max Accel:"; + const static PROGMEM prog_uchar message1aMaxAccelRate[] = "A Max Accel:"; + const static PROGMEM prog_uchar message1ExtruderNorm[] = "Acc Norm Move:"; + const static PROGMEM prog_uchar message1ExtruderRetract[] = "Acc Extr Move:"; + const static PROGMEM prog_uchar message1MinFeedRate[] = "Min Feed Rate:"; + const static PROGMEM prog_uchar message1MinTravelFeedRate[] = "MinTrvlFeedRate:"; + const static PROGMEM prog_uchar message1MaxXYJerk[] = "Max XY Jerk:"; + const static PROGMEM prog_uchar message1MaxZJerk[] = "Max Z Jerk:"; + const static PROGMEM prog_uchar message1AdvanceK[] = "Advance K:"; + const static PROGMEM prog_uchar message1FilamentDiameter[] = "Filament Dia:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if ( accelerateSettingsState != lastAccelerateSettingsState ) forceRedraw = true; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + switch(accelerateSettingsState) { + case AS_MAX_FEEDRATE_X: + lcd.writeFromPgmspace(message1xMaxFeedRate); + break; + case AS_MAX_FEEDRATE_Y: + lcd.writeFromPgmspace(message1yMaxFeedRate); + break; + case AS_MAX_FEEDRATE_Z: + lcd.writeFromPgmspace(message1zMaxFeedRate); + break; + case AS_MAX_FEEDRATE_A: + lcd.writeFromPgmspace(message1aMaxFeedRate); + break; + case AS_MAX_ACCELERATION_X: + lcd.writeFromPgmspace(message1xMaxAccelRate); + break; + case AS_MAX_ACCELERATION_Y: + lcd.writeFromPgmspace(message1yMaxAccelRate); + break; + case AS_MAX_ACCELERATION_Z: + lcd.writeFromPgmspace(message1zMaxAccelRate); + break; + case AS_MAX_ACCELERATION_A: + lcd.writeFromPgmspace(message1aMaxAccelRate); + break; + case AS_MAX_EXTRUDER_NORM: + lcd.writeFromPgmspace(message1ExtruderNorm); + break; + case AS_MAX_EXTRUDER_RETRACT: + lcd.writeFromPgmspace(message1ExtruderRetract); + break; + case AS_MIN_FEED_RATE: + lcd.writeFromPgmspace(message1MinFeedRate); + break; + case AS_MIN_TRAVEL_FEED_RATE: + lcd.writeFromPgmspace(message1MinTravelFeedRate); + break; + case AS_MAX_XY_JERK: + lcd.writeFromPgmspace(message1MaxXYJerk); + break; + case AS_MAX_Z_JERK: + lcd.writeFromPgmspace(message1MaxZJerk); + break; + case AS_ADVANCE_K: + lcd.writeFromPgmspace(message1AdvanceK); + break; + case AS_FILAMENT_DIAMETER: + lcd.writeFromPgmspace(message1FilamentDiameter); + break; + } + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + uint32_t value = 0; + + uint8_t currentIndex = accelerateSettingsState - AS_MAX_FEEDRATE_X; + + value = values[currentIndex]; + + lcd.setCursor(0,1); + + switch(accelerateSettingsState) { + case AS_MIN_FEED_RATE: + case AS_MIN_TRAVEL_FEED_RATE: + case AS_MAX_XY_JERK: + case AS_MAX_Z_JERK: + lcd.writeFloat((float)value / 10.0, 1); + break; + case AS_ADVANCE_K: + lcd.writeFloat((float)value / 100000.0, 5); + break; + case AS_FILAMENT_DIAMETER: + lcd.writeFloat((float)value / 100.0, 2); + break; + default: + lcd.writeFloat((float)value, 0); + break; + } + + lcd.writeFromPgmspace(blank); + + lastAccelerateSettingsState = accelerateSettingsState; +} + +void AcceleratedSettingsMode::notifyButtonPressed(ButtonArray::ButtonName button) { + if (( accelerateSettingsState == AS_FILAMENT_DIAMETER ) && (button == ButtonArray::OK )) { + //Write the data + cli(); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, values[0]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, values[1]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, values[2]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, values[3]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, values[4]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, values[5]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, values[6]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, values[7]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, values[8]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, values[9]); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE, values[10]); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE, values[11]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_XY_JERK, values[12]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_Z_JERK, values[13]); + eeprom::putEepromUInt32(eeprom::ACCEL_ADVANCE_K, values[14]); + eeprom::putEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER, values[15]); + sei(); + + host::stopBuild(); + return; + } + + uint8_t currentIndex = accelerateSettingsState - AS_MAX_FEEDRATE_X; + + uint32_t lastValue = values[currentIndex]; + + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + accelerateSettingsState = (enum accelerateSettingsState)((uint8_t)accelerateSettingsState + 1); + return; + break; + case ButtonArray::ZPLUS: + // increment more + values[currentIndex] += 100; + break; + case ButtonArray::ZMINUS: + // decrement more + values[currentIndex] -= 100; + break; + case ButtonArray::YPLUS: + // increment less + values[currentIndex] += 1; + break; + case ButtonArray::YMINUS: + // decrement less + values[currentIndex] -= 1; + break; + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } + + if (!(( accelerateSettingsState == AS_MIN_FEED_RATE ) || ( accelerateSettingsState == AS_MIN_TRAVEL_FEED_RATE ) || ( accelerateSettingsState == AS_ADVANCE_K ))) { + if ( values[currentIndex] < 1 ) values[currentIndex] = 1; + } + + if ( values[currentIndex] > 200000 ) values[currentIndex] = 1; +} + +AccelerationMenu::AccelerationMenu() { + itemCount = 3; + + reset(); +} + +void AccelerationMenu::resetState() { + if ( eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0) ) acceleration = true; + else acceleration = false; + + if ( acceleration ) itemCount = 3; + else itemCount = 1; + + itemIndex = 0; + firstItemIndex = 0; +} + +void AccelerationMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg1[] = "Stepper Driver"; + const static PROGMEM prog_uchar msg2[] = "Accel. Settings"; + const static PROGMEM prog_uchar msg3[] = "Extdr. Steps/mm"; + + if (( ! acceleration ) && ( index > 0 )) return; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg1); + break; + case 1: + lcd.writeFromPgmspace(msg2); + break; + case 2: + lcd.writeFromPgmspace(msg3); + break; + } +} + +void AccelerationMenu::handleSelect(uint8_t index) { + if (( ! acceleration ) && ( index > 0 )) return; + + switch (index) { + case 0: + interface::pushScreen(&stepperDriverAcceleratedMenu); + break; + case 1: + interface::pushScreen(&acceleratedSettingsMode); + break; + case 2: + interface::pushScreen(&eStepsPerMMMode); + break; + } +} + +void EStepsPerMMMode::reset() { + value = eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, 44); + if ( value < 1 ) { + eeprom::putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, 44); + value = eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, 44); //Just in case + } +} + +void EStepsPerMMMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Extrdr Steps/mm:"; + const static PROGMEM prog_uchar message2[] = "(calib)"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(message2); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(8,2); + lcd.writeFloat((float)value / 10.0,1); + lcd.writeFromPgmspace(blank); +} + +void EStepsPerMMMode::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + eeprom::putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,value); + //Reset to read in the new value + host::stopBuild(); + return; + break; + case ButtonArray::ZPLUS: + // increment more + value += 25; + break; + case ButtonArray::ZMINUS: + // decrement more + value -= 25; + break; + case ButtonArray::YPLUS: + // increment less + value += 1; + break; + case ButtonArray::YMINUS: + // decrement less + value -= 1; + break; + + case ButtonArray::XMINUS: + interface::pushScreen(&eStepsPerMMStepsMode); + break; + case ButtonArray::XPLUS: + break; + } + + if (( value < 1 ) || ( value > 200000 )) value = 1; +} + +void EStepsPerMMStepsMode::reset() { + value = 200; + steppers::switchToRegularDriver(); + overrideExtrudeSeconds = 0; +} + +void EStepsPerMMStepsMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Extrude N steps:"; + const static PROGMEM prog_uchar message2[] = "(extrude)"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if (overrideExtrudeSeconds) extrude(true); + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(message2); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(10,2); + lcd.writeFloat((float)value,0); + lcd.writeFromPgmspace(blank); +} + +void EStepsPerMMStepsMode::extrude(bool overrideTempCheck) { + //Check we're hot enough + if ( ! overrideTempCheck ) + { + OutPacket responsePacket; + if (extruderControl(SLAVE_CMD_IS_TOOL_READY, EXTDR_CMD_GET, responsePacket, 0)) { + uint8_t data = responsePacket.read8(1); + + if ( ! data ) + { + overrideExtrudeSeconds = 1; + interface::pushScreen(&extruderTooColdMenu); + return; + } + } + } + + Point position = steppers::getPosition(); + + float rpm = (float)eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19) / 10.0; + + //60 * 1000000 = # uS in a minute + //200 * 8 = 200 steps per revolution * 1/8 stepping + int32_t interval = (int32_t)(60L * 1000000L) / (int32_t)((float)(200 * 8) * rpm); + + position[3] += -value; + steppers::setTarget(position, interval); + + if (overrideTempCheck) overrideExtrudeSeconds = 0; +} + + +void EStepsPerMMStepsMode::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + steppers::switchToAcceleratedDriver(); + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + steppers::switchToAcceleratedDriver(); + eStepsPerMMLengthMode.steps = value; + interface::pushScreen(&eStepsPerMMLengthMode); + break; + case ButtonArray::ZPLUS: + // increment more + value += 25; + break; + case ButtonArray::ZMINUS: + // decrement more + value -= 25; + break; + case ButtonArray::YPLUS: + // increment less + value += 1; + break; + case ButtonArray::YMINUS: + // decrement less + value -= 1; + break; + + case ButtonArray::XMINUS: + extrude(false); + break; + case ButtonArray::XPLUS: + break; + } +} + +void EStepsPerMMLengthMode::reset() { + value = 1; +} + +void EStepsPerMMLengthMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Enter noodle"; + const static PROGMEM prog_uchar message2[] = "length in mm's"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,1); + lcd.writeFromPgmspace(message2); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(0,2); + lcd.writeFloat((float)value,0); + lcd.writeFromPgmspace(blank); +} + +void EStepsPerMMLengthMode::notifyButtonPressed(ButtonArray::ButtonName button) { + uint32_t espm; + + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + if ( steps < 0 ) steps *= -1; + espm = (uint32_t)lround(((float)steps / (float)value) * 10.0); + eeprom::putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,espm); + + //Reset to read in the new value + host::stopBuild(); + return; + break; + case ButtonArray::ZPLUS: + // increment more + value += 5; + break; + case ButtonArray::ZMINUS: + // decrement more + value -= 5; + break; + case ButtonArray::YPLUS: + // increment less + value += 1; + break; + case ButtonArray::YMINUS: + // decrement less + value -= 1; + break; + + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } + + if (( value < 1 ) || ( value > 200000 )) value = 1; +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 39b5d37..41d7ec8 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -688,11 +688,125 @@ protected: void handleSelect(uint8_t index); }; +class StepperDriverAcceleratedMenu: public Menu { +public: + StepperDriverAcceleratedMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + +class AcceleratedSettingsMode: public Screen { +private: + enum accelerateSettingsState { + AS_NONE, + AS_MAX_FEEDRATE_X, + AS_MAX_FEEDRATE_Y, + AS_MAX_FEEDRATE_Z, + AS_MAX_FEEDRATE_A, + AS_MAX_ACCELERATION_X, + AS_MAX_ACCELERATION_Y, + AS_MAX_ACCELERATION_Z, + AS_MAX_ACCELERATION_A, + AS_MAX_EXTRUDER_NORM, + AS_MAX_EXTRUDER_RETRACT, + AS_MIN_FEED_RATE, + AS_MIN_TRAVEL_FEED_RATE, + AS_MAX_XY_JERK, + AS_MAX_Z_JERK, + AS_ADVANCE_K, + AS_FILAMENT_DIAMETER, + }; + + enum accelerateSettingsState accelerateSettingsState, lastAccelerateSettingsState; + + uint32_t values[16]; + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + +class EStepsPerMMLengthMode: public Screen { +private: + uint32_t value; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); + + int32_t steps; +}; + +class EStepsPerMMStepsMode: public Screen { +private: + int32_t value; + ExtruderTooColdMenu extruderTooColdMenu; + EStepsPerMMLengthMode eStepsPerMMLengthMode; + + void extrude(bool overrideTempCheck); + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + +class EStepsPerMMMode: public Screen { +private: + uint32_t value; + EStepsPerMMStepsMode eStepsPerMMStepsMode; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + +class AccelerationMenu: public Menu { +private: + StepperDriverAcceleratedMenu stepperDriverAcceleratedMenu; + AcceleratedSettingsMode acceleratedSettingsMode; + EStepsPerMMMode eStepsPerMMMode; + + bool acceleration; +public: + AccelerationMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + class BuildSettingsMenu: public Menu { private: PreheatDuringEstimateMenu preheatDuringEstimateMenu; OverrideGCodeTempMenu overrideGCodeTempMenu; ABPCopiesSetScreen abpCopiesSetScreen; + AccelerationMenu accelerationMenu; public: BuildSettingsMenu(); diff --git a/firmware/src/shared/StepperAccel.cc b/firmware/src/shared/StepperAccel.cc new file mode 100644 index 0000000..e051612 --- /dev/null +++ b/firmware/src/shared/StepperAccel.cc @@ -0,0 +1,537 @@ +/* + StepperAccel.cc - stepper motor driver: executes motion plans using stepper motors + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + + Grbl 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, or + (at your option) any later version. + + Grbl 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 Grbl. If not, see . +*/ + +/* The timer calculations of this module informed by the 'RepRap cartesian firmware' by Zack Smith + and Philipp Tiefenbacher. */ + +#include "Configuration.hh" + +#ifdef HAS_STEPPER_ACCELERATION + + + +#include "StepperAccel.hh" +#include "StepperAccelSpeedTable.hh" +#include "StepperInterface.hh" +#include "Motherboard.hh" + +#include + + + + +//=========================================================================== +//=============================public variables ============================ +//=========================================================================== +block_t *current_block; // A pointer to the block currently being traced + + +//=========================================================================== +//=============================private variables ============================ +//=========================================================================== +//static makes it inpossible to be called from outside of this file by extern.! + +// Variables used by The Stepper Driver Interrupt +static unsigned char out_bits; // The next stepping-bits to be output +static int32_t counter_x, // Counter variables for the bresenham line tracer + counter_y, + counter_z, + counter_e; +volatile static uint32_t step_events_completed; // The number of step events executed in the current block +#ifdef ADVANCE + static int32_t advance_rate = 0, advance, final_advance = 0; + static int32_t old_advance = 0; +#endif +volatile static int32_t e_steps[3]; +volatile static unsigned char busy = false; // TRUE when SIG_OUTPUT_COMPARE1A is being serviced. Used to avoid retriggering that handler. +static int32_t acceleration_time, deceleration_time; +//static uint32_t accelerate_until, decelerate_after, acceleration_rate, initial_rate, final_rate, nominal_rate; +static unsigned short acc_step_rate; // needed for deccelaration start point +static char step_loops; +static unsigned short OCR1A_nominal; + +volatile int32_t count_position[NUM_AXIS] = { 0, 0, 0, 0}; +volatile char count_direction[NUM_AXIS] = { 1, 1, 1, 1}; + +static StepperInterface *stepperInterface; + +//=========================================================================== +//=============================functions ============================ +//=========================================================================== + +// intRes = intIn1 * intIn2 >> 16 +// uses: +// r26 to store 0 +// r27 to store the byte 1 of the 24 bit result +#define MultiU16X8toH16(intRes, charIn1, intIn2) \ +asm volatile ( \ +"clr r26 \n\t" \ +"mul %A1, %B2 \n\t" \ +"movw %A0, r0 \n\t" \ +"mul %A1, %A2 \n\t" \ +"add %A0, r1 \n\t" \ +"adc %B0, r26 \n\t" \ +"lsr r0 \n\t" \ +"adc %A0, r26 \n\t" \ +"adc %B0, r26 \n\t" \ +"clr r1 \n\t" \ +: \ +"=&r" (intRes) \ +: \ +"d" (charIn1), \ +"d" (intIn2) \ +: \ +"r26" \ +) + +// intRes = longIn1 * longIn2 >> 24 +// uses: +// r26 to store 0 +// r27 to store the byte 1 of the 48bit result +#define MultiU24X24toH16(intRes, longIn1, longIn2) \ +asm volatile ( \ +"clr r26 \n\t" \ +"mul %A1, %B2 \n\t" \ +"mov r27, r1 \n\t" \ +"mul %B1, %C2 \n\t" \ +"movw %A0, r0 \n\t" \ +"mul %C1, %C2 \n\t" \ +"add %B0, r0 \n\t" \ +"mul %C1, %B2 \n\t" \ +"add %A0, r0 \n\t" \ +"adc %B0, r1 \n\t" \ +"mul %A1, %C2 \n\t" \ +"add r27, r0 \n\t" \ +"adc %A0, r1 \n\t" \ +"adc %B0, r26 \n\t" \ +"mul %B1, %B2 \n\t" \ +"add r27, r0 \n\t" \ +"adc %A0, r1 \n\t" \ +"adc %B0, r26 \n\t" \ +"mul %C1, %A2 \n\t" \ +"add r27, r0 \n\t" \ +"adc %A0, r1 \n\t" \ +"adc %B0, r26 \n\t" \ +"mul %B1, %A2 \n\t" \ +"add r27, r1 \n\t" \ +"adc %A0, r26 \n\t" \ +"adc %B0, r26 \n\t" \ +"lsr r27 \n\t" \ +"adc %A0, r26 \n\t" \ +"adc %B0, r26 \n\t" \ +"clr r1 \n\t" \ +: \ +"=&r" (intRes) \ +: \ +"d" (longIn1), \ +"d" (longIn2) \ +: \ +"r26" , "r27" \ +) + +// Some useful constants + +#define ENABLE_STEPPER_DRIVER_INTERRUPT() TIMSK1 |= (1< +// +// The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates +// first block->accelerate_until step_events_completed, then keeps going at constant speed until +// step_events_completed reaches block->decelerate_after after which it decelerates until the trapezoid generator is reset. +// The slope of acceleration is calculated with the leib ramp alghorithm. + +void st_wake_up() { + // TCNT1 = 0; + if(busy == false) + ENABLE_STEPPER_DRIVER_INTERRUPT(); +} + +FORCE_INLINE unsigned short calc_timer(unsigned short step_rate) { + unsigned short timer; + if(step_rate > MAX_STEP_FREQUENCY) step_rate = MAX_STEP_FREQUENCY; + + if(step_rate > 20000) { // If steprate > 20kHz >> step 4 times + step_rate = (step_rate >> 2)&0x3fff; + step_loops = 4; + } + else if(step_rate > 10000) { // If steprate > 10kHz >> step 2 times + step_rate = (step_rate >> 1)&0x7fff; + step_loops = 2; + } + else { + step_loops = 1; + } + + if(step_rate < 32) step_rate = 32; + step_rate -= 32; // Correct for minimal speed + if(step_rate >= (8*256)){ // higher step rate + unsigned short table_address = (unsigned short)&speed_lookuptable_fast[(unsigned char)(step_rate>>8)][0]; + unsigned char tmp_step_rate = (step_rate & 0x00ff); + unsigned short gain = (unsigned short)pgm_read_word_near(table_address+2); + MultiU16X8toH16(timer, tmp_step_rate, gain); + timer = (unsigned short)pgm_read_word_near(table_address) - timer; + } + else { // lower step rates + unsigned short table_address = (unsigned short)&speed_lookuptable_slow[0][0]; + table_address += ((step_rate)>>1) & 0xfffc; + timer = (unsigned short)pgm_read_word_near(table_address); + timer -= (((unsigned short)pgm_read_word_near(table_address+2) * (unsigned char)(step_rate & 0x0007))>>3); + } + //if(timer < 100) { timer = 100; MSerial.print("Steprate to high : "); MSerial.println(step_rate); }//(20kHz this should never happen) + + //TEMPORARY WORKAROUND FOR ZIT BUG + if ( timer > 2000 ) timer = 2000; + + return timer; +} + + +//DEBUGGING +float zadvance; + +// Initializes the trapezoid generator from the current block. Called whenever a new +// block begins. +FORCE_INLINE void trapezoid_generator_reset() { + #ifdef ADVANCE + advance = current_block->initial_advance; + final_advance = current_block->final_advance; + // Do E steps + advance steps + e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); + old_advance = advance >>8; + zadvance = advance; + #endif + deceleration_time = 0; + // step_rate to timer interval + acc_step_rate = current_block->initial_rate; + acceleration_time = calc_timer(acc_step_rate); + OCR1A = acceleration_time; + OCR1A_nominal = calc_timer(current_block->nominal_rate); + #ifdef Z_LATE_ENABLE + if(current_block->steps_z > 0) stepperInterface[Z_AXIS].setEnabled(true); + #endif +} + +// "The Stepper Driver Interrupt" - This timer interrupt is the workhorse. +// It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. +void st_interrupt() +{ + // If there is no current block, attempt to pop one from the buffer + if (current_block == NULL) { + // Anything in the buffer? + current_block = plan_get_current_block(); + if (current_block != NULL) { + trapezoid_generator_reset(); + counter_x = -(current_block->step_event_count >> 1); + counter_y = counter_x; + counter_z = counter_x; + counter_e = counter_x; + step_events_completed = 0; +// #ifdef ADVANCE +// e_steps[current_block->active_extruder] = 0; +// #endif + } + else { + OCR1A=2000; // 1kHz. + } + } + + if (current_block != NULL) { + // Set directions TO DO This should be done once during init of trapezoid. Endstops -> interrupt + out_bits = current_block->direction_bits; + + // Set direction en check limit switches + if ((out_bits & (1<steps_e; + if (counter_e > 0) { + counter_e -= current_block->step_event_count; + if ((out_bits & (1<active_extruder]--; + } + else { + e_steps[current_block->active_extruder]++; + } + } + #endif //ADVANCE + + counter_x += current_block->steps_x; + if (counter_x > 0) { + stepperInterface[X_AXIS].step(true); + counter_x -= current_block->step_event_count; + stepperInterface[X_AXIS].step(false); + count_position[X_AXIS]+=count_direction[X_AXIS]; + } + + counter_y += current_block->steps_y; + if (counter_y > 0) { + stepperInterface[Y_AXIS].step(true); + counter_y -= current_block->step_event_count; + stepperInterface[Y_AXIS].step(false); + count_position[Y_AXIS]+=count_direction[Y_AXIS]; + } + + counter_z += current_block->steps_z; + if (counter_z > 0) { + stepperInterface[Z_AXIS].step(true); + counter_z -= current_block->step_event_count; + stepperInterface[Z_AXIS].step(false); + count_position[Z_AXIS]+=count_direction[Z_AXIS]; + } + + #ifndef ADVANCE + counter_e += current_block->steps_e; + if (counter_e > 0) { + stepperInterface[E_AXIS].step(true); + counter_e -= current_block->step_event_count; + stepperInterface[E_AXIS].step(false); + count_position[E_AXIS]+=count_direction[E_AXIS]; + } + #endif //!ADVANCE + step_events_completed += 1; + if(step_events_completed >= current_block->step_event_count) break; + } + // Calculare new timer value + unsigned short timer; + unsigned short step_rate; + if (step_events_completed <= (uint32_t)current_block->accelerate_until) { + + MultiU24X24toH16(acc_step_rate, acceleration_time, current_block->acceleration_rate); + acc_step_rate += current_block->initial_rate; + + // upper limit + if(acc_step_rate > current_block->nominal_rate) + acc_step_rate = current_block->nominal_rate; + + // step_rate to timer interval + timer = calc_timer(acc_step_rate); + OCR1A = timer; + acceleration_time += timer; + #ifdef ADVANCE + for(int8_t i=0; i < step_loops; i++) { + advance += advance_rate; + } + //if(advance > current_block->advance) advance = current_block->advance; + // Do E steps + advance steps + e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); + old_advance = advance >>8; + + #endif + } + else if (step_events_completed > (uint32_t)current_block->decelerate_after) { + MultiU24X24toH16(step_rate, deceleration_time, current_block->acceleration_rate); + + if(step_rate > acc_step_rate) { // Check step_rate stays positive + step_rate = current_block->final_rate; + } + else { + step_rate = acc_step_rate - step_rate; // Decelerate from aceleration end point. + } + + // lower limit + if(step_rate < current_block->final_rate) + step_rate = current_block->final_rate; + + // step_rate to timer interval + timer = calc_timer(step_rate); + OCR1A = timer; + deceleration_time += timer; + #ifdef ADVANCE + for(int8_t i=0; i < step_loops; i++) { + advance -= advance_rate; + } + if(advance < final_advance) advance = final_advance; + // Do E steps + advance steps + e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); + old_advance = advance >>8; + #endif //ADVANCE + } + else { + OCR1A = OCR1A_nominal; + } + + // If current block is finished, reset pointer + if (step_events_completed >= current_block->step_event_count) { + current_block = NULL; + plan_discard_current_block(); + } + } +} + +#ifdef ADVANCE +void st_advance_interrupt() + { + OCR4A = 100 * 16; + + // Set E direction (Depends on E direction + advance) + for(unsigned char i=0; i<4;i++) { + if (e_steps[0] != 0) { + stepperInterface[E_AXIS].step(false); + if (e_steps[0] < 0) { + stepperInterface[E_AXIS].setDirection(false); + e_steps[0]++; + stepperInterface[E_AXIS].step(true); + } + else if (e_steps[0] > 0) { + stepperInterface[E_AXIS].setDirection(true); + e_steps[0]--; + stepperInterface[E_AXIS].step(true); + } + } + #if EXTRUDERS > 1 + if (e_steps[1] != 0) { + //stepperInterface[?].step(false); + if (e_steps[1] < 0) { + stepperInterface[?].setDirection(false); + e_steps[1]++; + //stepperInterface[?].step(true); + } + else if (e_steps[1] > 0) { + stepperInterface[?].setDirection(true); + e_steps[1]--; + //stepperInterface[?].step(true); + } + } + #endif + #if EXTRUDERS > 2 + if (e_steps[2] != 0) { + //stepperInterface[?].step(false); + if (e_steps[2] < 0) { + //stepperInterface[?].setDirection(false); + e_steps[2]++; + //stepperInterface[?].step(true); + } + else if (e_steps[2] > 0) { + //stepperInterface[?].setDirection(true); + e_steps[2]--; + //stepperInterface[?].step(true); + } + } + #endif + } + } +#endif // ADVANCE + +void st_init() +{ + //Grab the stepper interfaces + stepperInterface = Motherboard::getBoard().getStepperAllInterfaces(); + + Motherboard::getBoard().setupAccelStepperTimer(); + + #ifdef ADVANCE + e_steps[0] = 0; + e_steps[1] = 0; + e_steps[2] = 0; + #endif //ADVANCE +} + +// Block until all buffered steps are executed +bool st_empty() +{ + if ( blocks_queued() ) return false; + return true; +} + +void st_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e) +{ + CRITICAL_SECTION_START; + count_position[X_AXIS] = x; + count_position[Y_AXIS] = y; + count_position[Z_AXIS] = z; + count_position[E_AXIS] = e; + CRITICAL_SECTION_END; +} + +void st_set_e_position(const int32_t &e) +{ + CRITICAL_SECTION_START; + count_position[E_AXIS] = e; + CRITICAL_SECTION_END; +} + +int32_t st_get_position(uint8_t axis) +{ + int32_t count_pos; + CRITICAL_SECTION_START; + count_pos = count_position[axis]; + CRITICAL_SECTION_END; + return count_pos; +} + +void quickStop() +{ + DISABLE_STEPPER_DRIVER_INTERRUPT(); + while(blocks_queued()) + plan_discard_current_block(); + ENABLE_STEPPER_DRIVER_INTERRUPT(); +} + +#endif diff --git a/firmware/src/shared/StepperAccel.hh b/firmware/src/shared/StepperAccel.hh new file mode 100644 index 0000000..5f269c0 --- /dev/null +++ b/firmware/src/shared/StepperAccel.hh @@ -0,0 +1,72 @@ +/* + StepperAccel.hh - stepper motor driver: executes motion plans of planner.c using the stepper motors + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + + Grbl 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, or + (at your option) any later version. + + Grbl 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 Grbl. If not, see . +*/ + +#ifndef STEPPERACCEL_HH +#define STEPPERACCEL_HH + +#include +#include "StepperAccelPlanner.hh" + +#define MAX_STEP_FREQUENCY 40000 // Max step frequency for Ultimaker (5000 pps / half step) + +#ifndef CRITICAL_SECTION_START + #define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli(); + #define CRITICAL_SECTION_END SREG = _sreg; +#endif //CRITICAL_SECTION_START + +enum AxisEnum {X_AXIS=0, Y_AXIS=1, Z_AXIS=2, E_AXIS=3}; + + +// of the buffer and all stops. This should not be much greater than zero and should only be changed +// if unwanted behavior is observed on a user's machine when running at very slow speeds. +#define MINIMUM_PLANNER_SPEED 2.0 // (mm/sec) + +const int dropsegments=5; //everything with less than this number of steps will be ignored as move and joined with the next movement + +#define EXTRUDERS 1 + +// Initialize and start the stepper motor subsystem +void st_init(); + +// Returns true is there are no buffered steps to be executed +bool st_empty(); + +// Set current position in steps +void st_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e); +void st_set_e_position(const int32_t &e); + +// Get current position in steps +int32_t st_get_position(uint8_t axis); + +// The stepper subsystem goes to sleep when it runs out of things to execute. Call this +// to notify the subsystem that it is time to go to work. +void st_wake_up(); + +void st_interrupt(); + +void st_advance_interrupt(); + +extern block_t *current_block; // A pointer to the block currently being traced + +void quickStop(); + +//DEBUGGING +extern float zadvance; +#endif diff --git a/firmware/src/shared/StepperAccelPlanner.cc b/firmware/src/shared/StepperAccelPlanner.cc new file mode 100644 index 0000000..b4c1ce5 --- /dev/null +++ b/firmware/src/shared/StepperAccelPlanner.cc @@ -0,0 +1,650 @@ +/* + StepperAccelPlanner.cc - buffers movement commands and manages the acceleration profile plan + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + + Grbl 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, or + (at your option) any later version. + + Grbl 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 Grbl. If not, see . +*/ + +/* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */ + +/* + Reasoning behind the mathematics in this module (in the key of 'Mathematica'): + + s == speed, a == acceleration, t == time, d == distance + + Basic definitions: + + Speed[s_, a_, t_] := s + (a*t) + Travel[s_, a_, t_] := Integrate[Speed[s, a, t], t] + + Distance to reach a specific speed with a constant acceleration: + + Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, d, t] + d -> (m^2 - s^2)/(2 a) --> estimate_acceleration_distance() + + Speed after a given distance of travel with constant acceleration: + + Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, m, t] + m -> Sqrt[2 a d + s^2] + + DestinationSpeed[s_, a_, d_] := Sqrt[2 a d + s^2] + + When to start braking (di) to reach a specified destionation speed (s2) after accelerating + from initial speed s1 without ever stopping at a plateau: + + Solve[{DestinationSpeed[s1, a, di] == DestinationSpeed[s2, a, d - di]}, di] + di -> (2 a d - s1^2 + s2^2)/(4 a) --> intersection_distance() + + IntersectionDistance[s1_, s2_, a_, d_] := (2 a d - s1^2 + s2^2)/(4 a) +*/ + +#include "Configuration.hh" + +#ifdef HAS_STEPPER_ACCELERATION + + +#include "StepperAccelPlanner.hh" +#include +#include +#include +#include +#include "StepperAccel.hh" +#include "StepperInterface.hh" +#include "Motherboard.hh" + +#ifdef abs +#undef abs +#endif + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) +#define abs(x) ((x)>0?(x):-(x)) + +#define ZSQUARE(x) ((x)*(x)) + +#define VEPSILON 1.0e-5 + +// v1 != v2 +#define VNEQ(v1,v2) (abs((v1)-(v2)) > VEPSILON) + +//=========================================================================== +//=============================public variables ============================ +//=========================================================================== + +uint32_t minsegmenttime; +float max_feedrate[4]; // set the max speeds +float axis_steps_per_unit[4]; +uint32_t max_acceleration_units_per_sq_second[4]; // Use M201 to override by software +float minimumfeedrate; +float p_acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX +float p_retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX +float max_xy_jerk; //speed than can be stopped at once, if i understand correctly. +float max_xy_jerk_squared; // max_xy_jerk * max_xy_jerk +float max_z_jerk; +float mintravelfeedrate; +uint32_t axis_steps_per_sqr_second[NUM_AXIS]; +float extrution_area, extruder_advance_k, steps_per_cubic_mm_e; + +// The current position of the tool in absolute steps +int32_t position[4]; //rescaled from extern when axis_steps_per_unit are changed by gcode +static float previous_speed[4]; // Speed of previous path line segment +static float previous_nominal_speed; // Nominal speed of previous path line segment + +static StepperInterface *stepperInterface; + +//=========================================================================== +//=================semi-private variables, used in inline functions ===== +//=========================================================================== +block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions +volatile unsigned char block_buffer_head; // Index of the next block to be pushed +volatile unsigned char block_buffer_tail; // Index of the block to process now + + +// Returns the index of the next block in the ring buffer +// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication. +static int8_t next_block_index(int8_t block_index) { + block_index++; + if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; } + return(block_index); +} + + +// Returns the index of the previous block in the ring buffer +static int8_t prev_block_index(int8_t block_index) { + if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; } + block_index--; + return(block_index); +} + +//=========================================================================== +//=============================functions ============================ +//=========================================================================== + +// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the +// given acceleration: +FORCE_INLINE float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration) +{ + if (acceleration!=0) { + return((target_rate*target_rate-initial_rate*initial_rate)/ + (2.0*acceleration)); + } + else { + return 0.0; // acceleration was 0, set acceleration distance to 0 + } +} + +// This function gives you the point at which you must start braking (at the rate of -acceleration) if +// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after +// a total travel of distance. This can be used to compute the intersection point between acceleration and +// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) + +FORCE_INLINE float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance) +{ + if (acceleration!=0) { + return((2.0*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/ + (4.0*acceleration) ); + } + else { + return 0.0; // acceleration was 0, set intersection distance to 0 + } +} + +// Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors. + +void calculate_trapezoid_for_block(block_t *block, float entry_factor, float exit_factor) { + uint32_t initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min) + uint32_t final_rate = ceil(block->nominal_rate*exit_factor); // (step/min) + + // Limit minimal step rate (Otherwise the timer will overflow.) + if(initial_rate <120) {initial_rate=120; } + if(final_rate < 120) {final_rate=120; } + + int32_t acceleration = block->acceleration_st; + int32_t accelerate_steps = + ceil(estimate_acceleration_distance(block->initial_rate, block->nominal_rate, acceleration)); + int32_t decelerate_steps = + floor(estimate_acceleration_distance(block->nominal_rate, block->final_rate, -acceleration)); + + // Calculate the size of Plateau of Nominal Rate. + int32_t plateau_steps = block->step_event_count-accelerate_steps-decelerate_steps; + + // Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will + // have to use intersection_distance() to calculate when to abort acceleration and start braking + // in order to reach the final_rate exactly at the end of this block. + if (plateau_steps < 0) { + accelerate_steps = ceil( + intersection_distance(block->initial_rate, block->final_rate, acceleration, block->step_event_count)); + accelerate_steps = max(accelerate_steps,0); // Check limits due to numerical round-off + accelerate_steps = min(accelerate_steps,block->step_event_count); + plateau_steps = 0; + } + + #ifdef ADVANCE + volatile int32_t initial_advance = block->advance*entry_factor*entry_factor; + volatile int32_t final_advance = block->advance*exit_factor*exit_factor; + #endif // ADVANCE + + // block->accelerate_until = accelerate_steps; + // block->decelerate_after = accelerate_steps+plateau_steps; + CRITICAL_SECTION_START; // Fill variables used by the stepper in a critical section + if(block->busy == false) { // Don't update variables if block is busy. + block->accelerate_until = accelerate_steps; + block->decelerate_after = accelerate_steps+plateau_steps; + block->initial_rate = initial_rate; + block->final_rate = final_rate; + #ifdef ADVANCE + block->initial_advance = initial_advance; + block->final_advance = final_advance; + #endif //ADVANCE + } + CRITICAL_SECTION_END; +} + +// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the +// acceleration within the allotted distance. +FORCE_INLINE float max_allowable_speed(float acceleration, float target_velocity, float distance) { + return sqrt(target_velocity*target_velocity-2*acceleration*distance); +} + +// The kernel called by planner_recalculate() when scanning the plan from last to first entry. +void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *next) { + if(!current) { return; } + + if (next) { + // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising. + // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and + // check for maximum allowable speed reductions to ensure maximum possible planned speed. + if (VNEQ(current->entry_speed, current->max_entry_speed)) { + + // If nominal length true, max junction speed is guaranteed to be reached. Only compute + // for max allowable speed if block is decelerating and nominal length is false. + if ((!current->nominal_length_flag) && (current->max_entry_speed > next->entry_speed)) { + current->entry_speed = min( current->max_entry_speed, + max_allowable_speed(-current->acceleration,next->entry_speed,current->millimeters)); + } else { + current->entry_speed = current->max_entry_speed; + } + current->recalculate_flag = true; + + } + } // Skip last block. Already initialized and set for recalculation. +} + +// planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This +// implements the reverse pass. +void planner_reverse_pass() { + uint8_t block_index = block_buffer_head; + if(((block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1)) > 3) { + block_index = (block_buffer_head - 3) & (BLOCK_BUFFER_SIZE - 1); + block_t *block[3] = { NULL, NULL, NULL }; + while(block_index != block_buffer_tail) { + block_index = prev_block_index(block_index); + block[2]= block[1]; + block[1]= block[0]; + block[0] = &block_buffer[block_index]; + planner_reverse_pass_kernel(block[0], block[1], block[2]); + } + } +} + +// The kernel called by planner_recalculate() when scanning the plan from first to last entry. +void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *next) { + if(!previous) { return; } + + // If the previous block is an acceleration block, but it is not long enough to complete the + // full speed change within the block, we need to adjust the entry speed accordingly. Entry + // speeds have already been reset, maximized, and reverse planned by reverse planner. + // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. + if (!previous->nominal_length_flag) { + if (VNEQ(previous->entry_speed, current->entry_speed)) { + double entry_speed = min( current->entry_speed, + max_allowable_speed(-previous->acceleration,previous->entry_speed,previous->millimeters) ); + + // Check for junction speed change + if (VNEQ(current->entry_speed, entry_speed)) { + current->entry_speed = entry_speed; + current->recalculate_flag = true; + } + } + } +} + +// planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This +// implements the forward pass. +void planner_forward_pass() { + uint8_t block_index = block_buffer_tail; + block_t *block[3] = { NULL, NULL, NULL }; + + while(block_index != block_buffer_head) { + block[0] = block[1]; + block[1] = block[2]; + block[2] = &block_buffer[block_index]; + planner_forward_pass_kernel(block[0],block[1],block[2]); + block_index = next_block_index(block_index); + } + planner_forward_pass_kernel(block[1], block[2], NULL); +} + +// Recalculates the trapezoid speed profiles for all blocks in the plan according to the +// entry_factor for each junction. Must be called by planner_recalculate() after +// updating the blocks. +void planner_recalculate_trapezoids() { + int8_t block_index = block_buffer_tail; + block_t *current; + block_t *next = NULL; + + while(block_index != block_buffer_head) { + current = next; + next = &block_buffer[block_index]; + if (current) { + // Recalculate if current block entry or exit junction speed has changed. + if (current->recalculate_flag || next->recalculate_flag) { + // NOTE: Entry and exit factors always > 0 by all previous logic operations. + calculate_trapezoid_for_block(current, current->entry_speed/current->nominal_speed, + next->entry_speed/current->nominal_speed); + current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed + } + } + block_index = next_block_index( block_index ); + } + // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated. + if(next != NULL) { + calculate_trapezoid_for_block(next, next->entry_speed/next->nominal_speed, + MINIMUM_PLANNER_SPEED/next->nominal_speed); + next->recalculate_flag = false; + } +} + +// Recalculates the motion plan according to the following algorithm: +// +// 1. Go over every block in reverse order and calculate a junction speed reduction (i.e. block_t.entry_factor) +// so that: +// a. The junction jerk is within the set limit +// b. No speed reduction within one block requires faster deceleration than the one, true constant +// acceleration. +// 2. Go over every block in chronological order and dial down junction speed reduction values if +// a. The speed increase within one block would require faster accelleration than the one, true +// constant acceleration. +// +// When these stages are complete all blocks have an entry_factor that will allow all speed changes to +// be performed using only the one, true constant acceleration, and where no junction jerk is jerkier than +// the set limit. Finally it will: +// +// 3. Recalculate trapezoids for all blocks. + +void planner_recalculate() { + planner_reverse_pass(); + planner_forward_pass(); + planner_recalculate_trapezoids(); +} + + +void plan_init(float extruderAdvanceK, float filamentDiameter, float axis_steps_per_unit_e) { + stepperInterface = Motherboard::getBoard().getStepperAllInterfaces(); + block_buffer_head = 0; + block_buffer_tail = 0; + memset(position, 0, sizeof(position)); // clear position + previous_speed[0] = 0.0; + previous_speed[1] = 0.0; + previous_speed[2] = 0.0; + previous_speed[3] = 0.0; + previous_nominal_speed = 0.0; + + extruder_advance_k = extruderAdvanceK; + extrution_area = 0.25 * filamentDiameter * filamentDiameter * 3.14159; + steps_per_cubic_mm_e = axis_steps_per_unit_e / extrution_area; +} + + + +float junction_deviation = 0.1; +// Add a new linear movement to the buffer. steps x, y and z is the absolute position in +// steps. Microseconds specify how many microseconds the move should take to perform. To aid acceleration +// calculation the caller must also provide the physical length of the line in millimeters. +void plan_buffer_line(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e, float feed_rate, const uint8_t &extruder) +{ + // Calculate the buffer head after we push this byte + int next_buffer_head = next_block_index(block_buffer_head); + + // The target position of the tool in absolute steps + // Calculate target position in absolute steps + //this should be done after the wait, because otherwise a M92 code within the gcode disrupts this calculation somehow + int32_t target[4]; + target[X_AXIS] = x; + target[Y_AXIS] = y; + target[Z_AXIS] = z; + target[E_AXIS] = e; + + // Prepare to set up new block + block_t *block = &block_buffer[block_buffer_head]; + + // Mark block as not busy (Not executed by the stepper interrupt) + block->busy = false; + + // Number of steps for each axis + block->steps_x = labs(target[X_AXIS]-position[X_AXIS]); + block->steps_y = labs(target[Y_AXIS]-position[Y_AXIS]); + block->steps_z = labs(target[Z_AXIS]-position[Z_AXIS]); + block->steps_e = labs(target[E_AXIS]-position[E_AXIS]); + block->step_event_count = max(block->steps_x, max(block->steps_y, max(block->steps_z, block->steps_e))); + + // Bail if this is a zero-length block + if (block->step_event_count <=dropsegments) { return; }; + + // Compute direction bits for this block + block->direction_bits = 0; + if (target[X_AXIS] < position[X_AXIS]) { block->direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<active_extruder = extruder; + + //enable active axes + if(block->steps_x != 0) stepperInterface[X_AXIS].setEnabled(true); + if(block->steps_y != 0) stepperInterface[Y_AXIS].setEnabled(true); + if(block->steps_z != 0) stepperInterface[Z_AXIS].setEnabled(true); + + // Enable all + if(block->steps_e != 0) { + stepperInterface[E_AXIS].setEnabled(true); + } + + float delta_mm[4]; + delta_mm[X_AXIS] = (target[X_AXIS]-position[X_AXIS])/axis_steps_per_unit[X_AXIS]; + delta_mm[Y_AXIS] = (target[Y_AXIS]-position[Y_AXIS])/axis_steps_per_unit[Y_AXIS]; + delta_mm[Z_AXIS] = (target[Z_AXIS]-position[Z_AXIS])/axis_steps_per_unit[Z_AXIS]; + delta_mm[E_AXIS] = (target[E_AXIS]-position[E_AXIS])/axis_steps_per_unit[E_AXIS]; + if ( block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0 ) { + block->millimeters = abs(delta_mm[E_AXIS]); + } else { + block->millimeters = sqrt(ZSQUARE(delta_mm[X_AXIS]) + ZSQUARE(delta_mm[Y_AXIS]) + + ZSQUARE(delta_mm[Z_AXIS]) + ZSQUARE(delta_mm[E_AXIS])); + } + float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides + + // Calculate speed in mm/second for each axis. No divide by zero due to previous checks. + float inverse_second = feed_rate * inverse_millimeters; + + block->nominal_speed = block->millimeters * inverse_second; // (mm/sec) Always > 0 + block->nominal_rate = ceil(block->step_event_count * inverse_second); // (step/sec) Always > 0 + + + + + if (block->steps_e == 0) { + if(feed_rate 1) feed_rate = feed_rate*moves_queued / (BLOCK_BUFFER_SIZE * 0.5); +#endif + + // Calculate speed in mm/sec for each axis + float current_speed[4]; + for(int i=0; i < 4; i++) { + current_speed[i] = delta_mm[i] * inverse_second; + } + + // Limit speed per axis + float speed_factor = 1.0; //factor <=1 do decrease speed + for(int i=0; i < 4; i++) { + if(abs(current_speed[i]) > max_feedrate[i]) + speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i])); + } + + // Correct the speed + if( speed_factor < 1.0) { + for(int i=0; i < 4; i++) { + if(abs(current_speed[i]) > max_feedrate[i]) + speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i])); + } + for(unsigned char i=0; i < 4; i++) { + current_speed[i] *= speed_factor; + } + block->nominal_speed *= speed_factor; + block->nominal_rate *= speed_factor; + } + + // Compute and limit the acceleration rate for the trapezoid generator. + float steps_per_mm = block->step_event_count/block->millimeters; + if(block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0) { + block->acceleration_st = ceil(p_retract_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 + } + else { + block->acceleration_st = ceil(p_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 + // Limit acceleration per axis + if(((float)block->acceleration_st * (float)block->steps_x / (float)block->step_event_count) > axis_steps_per_sqr_second[X_AXIS]) + block->acceleration_st = axis_steps_per_sqr_second[X_AXIS]; + if(((float)block->acceleration_st * (float)block->steps_y / (float)block->step_event_count) > axis_steps_per_sqr_second[Y_AXIS]) + block->acceleration_st = axis_steps_per_sqr_second[Y_AXIS]; + if(((float)block->acceleration_st * (float)block->steps_e / (float)block->step_event_count) > axis_steps_per_sqr_second[E_AXIS]) + block->acceleration_st = axis_steps_per_sqr_second[E_AXIS]; + if(((float)block->acceleration_st * (float)block->steps_z / (float)block->step_event_count ) > axis_steps_per_sqr_second[Z_AXIS]) + block->acceleration_st = axis_steps_per_sqr_second[Z_AXIS]; + } + block->acceleration = block->acceleration_st / steps_per_mm; + block->acceleration_rate = (int32_t)((float)block->acceleration_st * 8.388608); + +#if 0 // Use old jerk for now + // Compute path unit vector + double unit_vec[3]; + + unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters; + unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters; + unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inverse_millimeters; + + // Compute maximum allowable entry speed at junction by centripetal acceleration approximation. + // Let a circle be tangent to both previous and current path line segments, where the junction + // deviation is defined as the distance from the junction to the closest edge of the circle, + // colinear with the circle center. The circular segment joining the two paths represents the + // path of centripetal acceleration. Solve for max velocity based on max acceleration about the + // radius of the circle, defined indirectly by junction deviation. This may be also viewed as + // path width or max_jerk in the previous grbl version. This approach does not actually deviate + // from path, but used as a robust way to compute cornering speeds, as it takes into account the + // nonlinearities of both the junction angle and junction velocity. + double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed + + // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. + if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) { + // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) + // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. + double cos_theta = - previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] + - previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] + - previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; + + // Skip and use default max junction speed for 0 degree acute junction. + if (cos_theta < 0.95) { + vmax_junction = min(previous_nominal_speed,block->nominal_speed); + // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. + if (cos_theta > -0.95) { + // Compute maximum junction velocity based on maximum acceleration and junction deviation + double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. + vmax_junction = min(vmax_junction, + sqrt(block->acceleration * junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) ); + } + } + } +#endif + // Start with a safe speed + float vmax_junction = max_xy_jerk/2; + if(abs(current_speed[Z_AXIS]) > max_z_jerk/2) + vmax_junction = max_z_jerk/2; + vmax_junction = min(vmax_junction, block->nominal_speed); + + if ((moves_queued > 1) && (previous_nominal_speed > 0.0)) { + float jerk_squared = ZSQUARE(current_speed[X_AXIS]-previous_speed[X_AXIS]) + ZSQUARE(current_speed[Y_AXIS]-previous_speed[Y_AXIS]); + + if((previous_speed[X_AXIS] != 0.0) || (previous_speed[Y_AXIS] != 0.0)) { + vmax_junction = block->nominal_speed; + } + if (jerk_squared > max_xy_jerk_squared) { + vmax_junction *= sqrt((max_xy_jerk_squared/jerk_squared)); + } + if(abs(current_speed[Z_AXIS] - previous_speed[Z_AXIS]) > max_z_jerk) { + vmax_junction *= (max_z_jerk/abs(current_speed[Z_AXIS] - previous_speed[Z_AXIS])); + } + } + block->max_entry_speed = vmax_junction; + + // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. + double v_allowable = max_allowable_speed(-block->acceleration,MINIMUM_PLANNER_SPEED,block->millimeters); + block->entry_speed = min(vmax_junction, v_allowable); + + // Initialize planner efficiency flags + // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. + // If a block can de/ac-celerate from nominal speed to zero within the length of the block, then + // the current block and next block junction speeds are guaranteed to always be at their maximum + // junction speeds in deceleration and acceleration, respectively. This is due to how the current + // block nominal speed limits both the current and next maximum junction speeds. Hence, in both + // the reverse and forward planners, the corresponding block junction speed will always be at the + // the maximum junction speed and may always be ignored for any speed reduction checks. + if (block->nominal_speed <= v_allowable) { block->nominal_length_flag = true; } + else { block->nominal_length_flag = false; } + block->recalculate_flag = true; // Always calculate trapezoid for new block + + // Update previous path unit_vector and nominal speed + memcpy(previous_speed, current_speed, sizeof(previous_speed)); // previous_speed[] = current_speed[] + previous_nominal_speed = block->nominal_speed; + + #ifdef ADVANCE + // Calculate advance rate + if((block->steps_e == 0) || (block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0) || ( extruder_advance_k == 0.0 )) { + block->advance_rate = 0; + block->advance = 0; + } + else { + int32_t acc_dist = estimate_acceleration_distance(0, block->nominal_rate, block->acceleration_st); + float advance = (steps_per_cubic_mm_e * extruder_advance_k) * + (current_speed[E_AXIS] * current_speed[E_AXIS] * extrution_area * extrution_area)*256; + block->advance = advance; + if(acc_dist == 0) { + block->advance_rate = 0; + } + else { + block->advance_rate = advance / (float)acc_dist; + } + } + #endif // ADVANCE + + + + + calculate_trapezoid_for_block(block, block->entry_speed/block->nominal_speed, + MINIMUM_PLANNER_SPEED/block->nominal_speed); + + // Move buffer head + block_buffer_head = next_buffer_head; + + // Update position + memcpy(position, target, sizeof(target)); // position[] = target[] + + planner_recalculate(); + st_wake_up(); +} + +void plan_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e) +{ + position[X_AXIS] = x; + position[Y_AXIS] = y; + position[Z_AXIS] = z; + position[E_AXIS] = e; + st_set_position(position[X_AXIS], position[Y_AXIS], position[Z_AXIS], position[E_AXIS]); + previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest. + previous_speed[0] = 0.0; + previous_speed[1] = 0.0; + previous_speed[2] = 0.0; + previous_speed[3] = 0.0; +} + +void plan_set_e_position(const int32_t &e) +{ + position[E_AXIS] = (int32_t)e; + st_set_e_position(position[E_AXIS]); +} + +uint8_t movesplanned() +{ + return (block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1); +} + +#endif diff --git a/firmware/src/shared/StepperAccelPlanner.hh b/firmware/src/shared/StepperAccelPlanner.hh new file mode 100644 index 0000000..92978fd --- /dev/null +++ b/firmware/src/shared/StepperAccelPlanner.hh @@ -0,0 +1,144 @@ +/* + StepperAccelPlanner.hh - buffers movement commands and manages the acceleration profile plan + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + + Grbl 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, or + (at your option) any later version. + + Grbl 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 Grbl. If not, see . +*/ + +// This module is to be considered a sub-module of stepper.c. Please don't include +// this file from any other module. + +#ifndef STEPPERACCELPLANNER_HH +#define STEPPERACCELPLANNER_HH + +#include + +// extruder advance constant (s2/mm3) +// +// advance (steps) = STEPS_PER_CUBIC_MM_E * EXTUDER_ADVANCE_K * cubic mm per second ^ 2 +// +// hooke's law says: force = k * distance +// bernoulli's priniciple says: v ^ 2 / 2 + g . h + pressure / density = constant +// so: v ^ 2 is proportional to number of steps we advance the extruder +#define ADVANCE + +#define SLOWDOWN + +#define NUM_AXIS 4 // The axis order in all axis related arrays is X, Y, Z, E + +// The number of linear motions that can be in the plan at any give time. +// THE BLOCK_BUFFER_SIZE NEEDS TO BE A POWER OF 2, i.g. 8,16,32 because shifts and ors are used to do the ringbuffering. +#define BLOCK_BUFFER_SIZE 32 // maximize block buffer + +#define FORCE_INLINE __attribute__((always_inline)) inline + + + +// This struct is used when buffering the setup for each linear movement "nominal" values are as specified in +// the source g-code and may never actually be reached if acceleration management is active. +typedef struct { + // Fields used by the bresenham algorithm for tracing the line + int32_t steps_x, steps_y, steps_z, steps_e; // Step count along each axis + uint32_t step_event_count; // The number of step events required to complete this block + int32_t accelerate_until; // The index of the step event on which to stop acceleration + int32_t decelerate_after; // The index of the step event on which to start decelerating + int32_t acceleration_rate; // The acceleration rate used for acceleration calculation + unsigned char direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) + unsigned char active_extruder; // Selects the active extruder + #ifdef ADVANCE + int32_t advance_rate; + volatile int32_t initial_advance; + volatile int32_t final_advance; + float advance; + #endif + + // Fields used by the motion planner to manage acceleration + // float speed_x, speed_y, speed_z, speed_e; // Nominal mm/minute for each axis + float nominal_speed; // The nominal speed for this block in mm/min + float entry_speed; // Entry speed at previous-current junction in mm/min + float max_entry_speed; // Maximum allowable junction entry speed in mm/min + float millimeters; // The total travel of this block in mm + float acceleration; // acceleration mm/sec^2 + unsigned char recalculate_flag; // Planner flag to recalculate trapezoids on entry junction + unsigned char nominal_length_flag; // Planner flag for nominal speed always reached + + // Settings for the trapezoid generator + uint32_t nominal_rate; // The nominal step rate for this block in step_events/sec + uint32_t initial_rate; // The jerk-adjusted step rate at start of block + uint32_t final_rate; // The minimal rate at exit + uint32_t acceleration_st; // acceleration steps/sec^2 + volatile char busy; +} block_t; + +// Initialize the motion plan subsystem +void plan_init(float extruderAdvanceK, float filamentDiameter, float axis_steps_per_unit_e); + +// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in +// steps. Feed rate specifies the speed of the motion. +void plan_buffer_line(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e, float feed_rate, const uint8_t &extruder); + +// Set position. Used for G92 instructions. +void plan_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e); +void plan_set_e_position(const int32_t &e); + +uint8_t movesplanned(); //return the nr of buffered moves + +extern uint32_t minsegmenttime; +extern float max_feedrate[4]; // set the max speeds +extern float axis_steps_per_unit[4]; +extern uint32_t max_acceleration_units_per_sq_second[4]; // Use M201 to override by software +extern float minimumfeedrate; +extern float p_acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX +extern float p_retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX +extern float max_xy_jerk; //speed than can be stopped at once, if i understand correctly. +extern float max_xy_jerk_squared; // max_xy_jerk * max_xy_jerk +extern float max_z_jerk; +extern float mintravelfeedrate; +extern uint32_t axis_steps_per_sqr_second[NUM_AXIS]; + +extern block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions +extern volatile unsigned char block_buffer_head; // Index of the next block to be pushed +extern volatile unsigned char block_buffer_tail; +// Called when the current block is no longer needed. Discards the block and makes the memory +// availible for new blocks. +FORCE_INLINE void plan_discard_current_block() +{ + if (block_buffer_head != block_buffer_tail) { + block_buffer_tail = (block_buffer_tail + 1) & (BLOCK_BUFFER_SIZE - 1); + } +} + +// Gets the current block. Returns NULL if buffer empty +FORCE_INLINE block_t *plan_get_current_block() +{ + if (block_buffer_head == block_buffer_tail) { + return(NULL); + } + block_t *block = &block_buffer[block_buffer_tail]; + block->busy = true; + return(block); +} + +// Gets the current block. Returns NULL if buffer empty +FORCE_INLINE bool blocks_queued() +{ + if (block_buffer_head == block_buffer_tail) { + return false; + } + else + return true; +} +#endif diff --git a/firmware/src/shared/StepperAccelSpeedTable.hh b/firmware/src/shared/StepperAccelSpeedTable.hh new file mode 100644 index 0000000..ea3ca69 --- /dev/null +++ b/firmware/src/shared/StepperAccelSpeedTable.hh @@ -0,0 +1,77 @@ +#ifndef STEPPERACCELSPEEDTABLE_HH +#define STEPPERACCELSPEEDTABLE_HH + +#include +#include + +const uint16_t speed_lookuptable_fast[256][2] PROGMEM = {\ +{ 62500, 55556}, { 6944, 3268}, { 3676, 1176}, { 2500, 607}, { 1893, 369}, { 1524, 249}, { 1275, 179}, { 1096, 135}, +{ 961, 105}, { 856, 85}, { 771, 69}, { 702, 58}, { 644, 49}, { 595, 42}, { 553, 37}, { 516, 32}, +{ 484, 28}, { 456, 25}, { 431, 23}, { 408, 20}, { 388, 19}, { 369, 16}, { 353, 16}, { 337, 14}, +{ 323, 13}, { 310, 11}, { 299, 11}, { 288, 11}, { 277, 9}, { 268, 9}, { 259, 8}, { 251, 8}, +{ 243, 8}, { 235, 7}, { 228, 6}, { 222, 6}, { 216, 6}, { 210, 6}, { 204, 5}, { 199, 5}, +{ 194, 5}, { 189, 4}, { 185, 4}, { 181, 4}, { 177, 4}, { 173, 4}, { 169, 4}, { 165, 3}, +{ 162, 3}, { 159, 4}, { 155, 3}, { 152, 3}, { 149, 2}, { 147, 3}, { 144, 3}, { 141, 2}, +{ 139, 3}, { 136, 2}, { 134, 2}, { 132, 3}, { 129, 2}, { 127, 2}, { 125, 2}, { 123, 2}, +{ 121, 2}, { 119, 1}, { 118, 2}, { 116, 2}, { 114, 1}, { 113, 2}, { 111, 2}, { 109, 1}, +{ 108, 2}, { 106, 1}, { 105, 2}, { 103, 1}, { 102, 1}, { 101, 1}, { 100, 2}, { 98, 1}, +{ 97, 1}, { 96, 1}, { 95, 2}, { 93, 1}, { 92, 1}, { 91, 1}, { 90, 1}, { 89, 1}, +{ 88, 1}, { 87, 1}, { 86, 1}, { 85, 1}, { 84, 1}, { 83, 0}, { 83, 1}, { 82, 1}, +{ 81, 1}, { 80, 1}, { 79, 1}, { 78, 0}, { 78, 1}, { 77, 1}, { 76, 1}, { 75, 0}, +{ 75, 1}, { 74, 1}, { 73, 1}, { 72, 0}, { 72, 1}, { 71, 1}, { 70, 0}, { 70, 1}, +{ 69, 0}, { 69, 1}, { 68, 1}, { 67, 0}, { 67, 1}, { 66, 0}, { 66, 1}, { 65, 0}, +{ 65, 1}, { 64, 1}, { 63, 0}, { 63, 1}, { 62, 0}, { 62, 1}, { 61, 0}, { 61, 1}, +{ 60, 0}, { 60, 0}, { 60, 1}, { 59, 0}, { 59, 1}, { 58, 0}, { 58, 1}, { 57, 0}, +{ 57, 1}, { 56, 0}, { 56, 0}, { 56, 1}, { 55, 0}, { 55, 1}, { 54, 0}, { 54, 0}, +{ 54, 1}, { 53, 0}, { 53, 0}, { 53, 1}, { 52, 0}, { 52, 0}, { 52, 1}, { 51, 0}, +{ 51, 0}, { 51, 1}, { 50, 0}, { 50, 0}, { 50, 1}, { 49, 0}, { 49, 0}, { 49, 1}, +{ 48, 0}, { 48, 0}, { 48, 1}, { 47, 0}, { 47, 0}, { 47, 0}, { 47, 1}, { 46, 0}, +{ 46, 0}, { 46, 1}, { 45, 0}, { 45, 0}, { 45, 0}, { 45, 1}, { 44, 0}, { 44, 0}, +{ 44, 0}, { 44, 1}, { 43, 0}, { 43, 0}, { 43, 0}, { 43, 1}, { 42, 0}, { 42, 0}, +{ 42, 0}, { 42, 1}, { 41, 0}, { 41, 0}, { 41, 0}, { 41, 0}, { 41, 1}, { 40, 0}, +{ 40, 0}, { 40, 0}, { 40, 0}, { 40, 1}, { 39, 0}, { 39, 0}, { 39, 0}, { 39, 0}, +{ 39, 1}, { 38, 0}, { 38, 0}, { 38, 0}, { 38, 0}, { 38, 1}, { 37, 0}, { 37, 0}, +{ 37, 0}, { 37, 0}, { 37, 0}, { 37, 1}, { 36, 0}, { 36, 0}, { 36, 0}, { 36, 0}, +{ 36, 1}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 1}, +{ 34, 0}, { 34, 0}, { 34, 0}, { 34, 0}, { 34, 0}, { 34, 1}, { 33, 0}, { 33, 0}, +{ 33, 0}, { 33, 0}, { 33, 0}, { 33, 0}, { 33, 1}, { 32, 0}, { 32, 0}, { 32, 0}, +{ 32, 0}, { 32, 0}, { 32, 0}, { 32, 0}, { 32, 1}, { 31, 0}, { 31, 0}, { 31, 0}, +{ 31, 0}, { 31, 0}, { 31, 0}, { 31, 1}, { 30, 0}, { 30, 0}, { 30, 0}, { 30, 0} +}; + +const uint16_t speed_lookuptable_slow[256][2] PROGMEM = {\ +{ 62500, 12500}, { 50000, 8334}, { 41666, 5952}, { 35714, 4464}, { 31250, 3473}, { 27777, 2777}, { 25000, 2273}, { 22727, 1894}, +{ 20833, 1603}, { 19230, 1373}, { 17857, 1191}, { 16666, 1041}, { 15625, 920}, { 14705, 817}, { 13888, 731}, { 13157, 657}, +{ 12500, 596}, { 11904, 541}, { 11363, 494}, { 10869, 453}, { 10416, 416}, { 10000, 385}, { 9615, 356}, { 9259, 331}, +{ 8928, 308}, { 8620, 287}, { 8333, 269}, { 8064, 252}, { 7812, 237}, { 7575, 223}, { 7352, 210}, { 7142, 198}, +{ 6944, 188}, { 6756, 178}, { 6578, 168}, { 6410, 160}, { 6250, 153}, { 6097, 145}, { 5952, 139}, { 5813, 132}, +{ 5681, 126}, { 5555, 121}, { 5434, 115}, { 5319, 111}, { 5208, 106}, { 5102, 102}, { 5000, 99}, { 4901, 94}, +{ 4807, 91}, { 4716, 87}, { 4629, 84}, { 4545, 81}, { 4464, 79}, { 4385, 75}, { 4310, 73}, { 4237, 71}, +{ 4166, 68}, { 4098, 66}, { 4032, 64}, { 3968, 62}, { 3906, 60}, { 3846, 59}, { 3787, 56}, { 3731, 55}, +{ 3676, 53}, { 3623, 52}, { 3571, 50}, { 3521, 49}, { 3472, 48}, { 3424, 46}, { 3378, 45}, { 3333, 44}, +{ 3289, 43}, { 3246, 41}, { 3205, 41}, { 3164, 39}, { 3125, 39}, { 3086, 38}, { 3048, 36}, { 3012, 36}, +{ 2976, 35}, { 2941, 35}, { 2906, 33}, { 2873, 33}, { 2840, 32}, { 2808, 31}, { 2777, 30}, { 2747, 30}, +{ 2717, 29}, { 2688, 29}, { 2659, 28}, { 2631, 27}, { 2604, 27}, { 2577, 26}, { 2551, 26}, { 2525, 25}, +{ 2500, 25}, { 2475, 25}, { 2450, 23}, { 2427, 24}, { 2403, 23}, { 2380, 22}, { 2358, 22}, { 2336, 22}, +{ 2314, 21}, { 2293, 21}, { 2272, 20}, { 2252, 20}, { 2232, 20}, { 2212, 20}, { 2192, 19}, { 2173, 18}, +{ 2155, 19}, { 2136, 18}, { 2118, 18}, { 2100, 17}, { 2083, 17}, { 2066, 17}, { 2049, 17}, { 2032, 16}, +{ 2016, 16}, { 2000, 16}, { 1984, 16}, { 1968, 15}, { 1953, 16}, { 1937, 14}, { 1923, 15}, { 1908, 15}, +{ 1893, 14}, { 1879, 14}, { 1865, 14}, { 1851, 13}, { 1838, 14}, { 1824, 13}, { 1811, 13}, { 1798, 13}, +{ 1785, 12}, { 1773, 13}, { 1760, 12}, { 1748, 12}, { 1736, 12}, { 1724, 12}, { 1712, 12}, { 1700, 11}, +{ 1689, 12}, { 1677, 11}, { 1666, 11}, { 1655, 11}, { 1644, 11}, { 1633, 10}, { 1623, 11}, { 1612, 10}, +{ 1602, 10}, { 1592, 10}, { 1582, 10}, { 1572, 10}, { 1562, 10}, { 1552, 9}, { 1543, 10}, { 1533, 9}, +{ 1524, 9}, { 1515, 9}, { 1506, 9}, { 1497, 9}, { 1488, 9}, { 1479, 9}, { 1470, 9}, { 1461, 8}, +{ 1453, 8}, { 1445, 9}, { 1436, 8}, { 1428, 8}, { 1420, 8}, { 1412, 8}, { 1404, 8}, { 1396, 8}, +{ 1388, 7}, { 1381, 8}, { 1373, 7}, { 1366, 8}, { 1358, 7}, { 1351, 7}, { 1344, 8}, { 1336, 7}, +{ 1329, 7}, { 1322, 7}, { 1315, 7}, { 1308, 6}, { 1302, 7}, { 1295, 7}, { 1288, 6}, { 1282, 7}, +{ 1275, 6}, { 1269, 7}, { 1262, 6}, { 1256, 6}, { 1250, 7}, { 1243, 6}, { 1237, 6}, { 1231, 6}, +{ 1225, 6}, { 1219, 6}, { 1213, 6}, { 1207, 6}, { 1201, 5}, { 1196, 6}, { 1190, 6}, { 1184, 5}, +{ 1179, 6}, { 1173, 5}, { 1168, 6}, { 1162, 5}, { 1157, 5}, { 1152, 6}, { 1146, 5}, { 1141, 5}, +{ 1136, 5}, { 1131, 5}, { 1126, 5}, { 1121, 5}, { 1116, 5}, { 1111, 5}, { 1106, 5}, { 1101, 5}, +{ 1096, 5}, { 1091, 5}, { 1086, 4}, { 1082, 5}, { 1077, 5}, { 1072, 4}, { 1068, 5}, { 1063, 4}, +{ 1059, 5}, { 1054, 4}, { 1050, 4}, { 1046, 5}, { 1041, 4}, { 1037, 4}, { 1033, 5}, { 1028, 4}, +{ 1024, 4}, { 1020, 4}, { 1016, 4}, { 1012, 4}, { 1008, 4}, { 1004, 4}, { 1000, 4}, { 996, 4}, +{ 992, 4}, { 988, 4}, { 984, 4}, { 980, 4}, { 976, 4}, { 972, 4}, { 968, 3}, { 965, 3} +}; + +#endif diff --git a/firmware/src/shared/StepperAxis.cc b/firmware/src/shared/StepperAxis.cc index 9bb3aa2..115b3fd 100644 --- a/firmware/src/shared/StepperAxis.cc +++ b/firmware/src/shared/StepperAxis.cc @@ -17,6 +17,7 @@ void StepperAxis::setTarget(const int32_t target_in, } else { delta = target - position; } + absoluteTarget = position + delta; direction = true; if (delta != 0) { interface->setEnabled(true); @@ -50,6 +51,7 @@ void StepperAxis::reset() { minimum = 0; maximum = 0; target = 0; + absoluteTarget = 0; counter = 0; delta = 0; #if defined(SINGLE_SWITCH_ENDSTOPS) && (SINGLE_SWITCH_ENDSTOPS == 1) @@ -152,3 +154,18 @@ bool StepperAxis::isAtMaximum() { bool StepperAxis::isAtMinimum() { return interface->isAtMinimum(); } + + +void StepperAxis::step() { + interface->setDirection(direction); + bool hit_endstop = checkEndstop(false); + if (!hit_endstop) interface->step(true); + if (direction) position++; + else position--; + interface->step(false); +} + + +void StepperAxis::setDirection(bool dir) { + direction = dir; +} diff --git a/firmware/src/shared/StepperAxis.hh b/firmware/src/shared/StepperAxis.hh index a74999b..4484106 100644 --- a/firmware/src/shared/StepperAxis.hh +++ b/firmware/src/shared/StepperAxis.hh @@ -15,7 +15,8 @@ public: volatile int32_t position; ///< Current position of this axis, in steps int32_t minimum; ///< Minimum position, in steps int32_t maximum; ///< Maximum position, in steps - volatile int32_t target; ///< Target position, in steps + volatile int32_t target; ///< Target position, in steps (relative or absolute position, depending on how called) + volatile int32_t absoluteTarget;///< Absolute Target position, in steps (absolute position) volatile int32_t counter; ///< Step counter; represents the proportion of ///< a step so far passed. When the counter hits ///< zero, a step is taken. @@ -94,6 +95,13 @@ public: /// \return True if the axis has triggered its minimum endstop bool isAtMinimum(); + //Methods for accelerated stepping + + //Move one step + void step(); + + //Set the direction for the steps + void setDirection(bool dir); }; #endif // STEPPERAXIS_HH From 99c7ce40cbb87d3b9da82135313821d697836233 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 17 Feb 2012 23:27:54 -0700 Subject: [PATCH 49/61] Added Marlin Acceleration. 100mm/s (and faster) is achievable on a ToM. --- firmware/src/Motherboard/Command.cc | 32 +- firmware/src/Motherboard/EepromMap.cc | 19 + firmware/src/Motherboard/EepromMap.hh | 32 +- firmware/src/Motherboard/Main.cc | 1 + firmware/src/Motherboard/Steppers.cc | 391 ++++++++-- firmware/src/Motherboard/Steppers.hh | 15 + .../Motherboard/boards/mb24/Configuration.hh | 3 + .../Motherboard/boards/mb24/Motherboard.cc | 84 ++- .../Motherboard/boards/mb24/Motherboard.hh | 18 +- firmware/src/shared/CircularBuffer.hh | 10 + firmware/src/shared/Eeprom.cc | 29 + firmware/src/shared/Eeprom.hh | 4 +- firmware/src/shared/InterfaceBoard.hh | 2 +- firmware/src/shared/Menu.cc | 709 +++++++++++++++++- firmware/src/shared/Menu.hh | 114 +++ firmware/src/shared/StepperAccel.cc | 537 +++++++++++++ firmware/src/shared/StepperAccel.hh | 72 ++ firmware/src/shared/StepperAccelPlanner.cc | 650 ++++++++++++++++ firmware/src/shared/StepperAccelPlanner.hh | 144 ++++ firmware/src/shared/StepperAccelSpeedTable.hh | 77 ++ firmware/src/shared/StepperAxis.cc | 17 + firmware/src/shared/StepperAxis.hh | 10 +- 22 files changed, 2860 insertions(+), 110 deletions(-) create mode 100644 firmware/src/shared/StepperAccel.cc create mode 100644 firmware/src/shared/StepperAccel.hh create mode 100644 firmware/src/shared/StepperAccelPlanner.cc create mode 100644 firmware/src/shared/StepperAccelPlanner.hh create mode 100644 firmware/src/shared/StepperAccelSpeedTable.hh diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index 2e1e6b4..736bd59 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -29,6 +29,10 @@ #include "SDCard.hh" #include "ExtruderControl.hh" +#ifdef HAS_STEPPER_ACCELERATION +#include "StepperAccel.hh" +#endif + namespace command { #define COMMAND_BUFFER_SIZE 512 @@ -129,6 +133,7 @@ enum { Timeout delay_timeout; Timeout homing_timeout; Timeout tool_wait_timeout; +bool acceleration; void reset() { pauseAtZPos(0.0); @@ -140,6 +145,10 @@ void reset() { firstHeatTool0 = true; firstHeatHbp = true; mode = READY; + + uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + if ( accel & 0x01 ) acceleration = true; + else acceleration = false; } @@ -373,6 +382,27 @@ void runCommandSlice() { mode = READY; } } + +#ifdef HAS_STEPPER_ACCELERATION + //If we're running acceleration, we want to populate the pipeline buffer, + //but we also need to sync (wait for the pipeline buffer to clear) on certain + //commands, we do that here + if (( acceleration ) && ( mode == READY ) && ( ! estimating )) { + if (command_buffer.getLength() > 0) { + uint8_t command = command_buffer.peek(); + + //If we're not pipeline'able command, then we sync here, + //by waiting for the pipeline buffer to empty before continuing + if ((command != HOST_CMD_QUEUE_POINT_ABS) && + (command != HOST_CMD_QUEUE_POINT_EXT) && + (command != HOST_CMD_QUEUE_POINT_NEW)) { + if ( ! st_empty() ) return; + } + } + else return; + } +#endif + Point p; if (mode == READY) { // process next command on the queue. @@ -496,7 +526,6 @@ void runCommandSlice() { if ( ! estimating ) tool_wait_timeout.start(toolTimeout*1000000L); } } else if (command == HOST_CMD_WAIT_FOR_PLATFORM) { - // FIXME: Almost equivalent to WAIT_FOR_TOOL if (command_buffer.getLength() >= 6) { mode = WAIT_ON_PLATFORM; command_buffer.pop(); @@ -506,7 +535,6 @@ void runCommandSlice() { if ( ! estimating ) tool_wait_timeout.start(toolTimeout*1000000L); } } else if (command == HOST_CMD_STORE_HOME_POSITION) { - // check for completion if (command_buffer.getLength() >= 2) { command_buffer.pop(); diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index 0d7970b..4478987 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -51,6 +51,25 @@ void setDefaults() { eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,1); eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,0); eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,0); + eeprom_write_byte((uint8_t*)eeprom::STEPPER_DRIVER,0); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X,160); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y,160); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z,10); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A,100); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_B,100); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X,2000); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y,2000); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z,150); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A,60000); + putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM,5000); + putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT,3000); + putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,44); //Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,0); //Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,0); //Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_MAX_XY_JERK,2); //30mm/s Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_MAX_Z_JERK,100); //10mm/s Multiplied by 10 + putEepromUInt32(eeprom::ACCEL_ADVANCE_K,50); //0.00001 Multiplied by 100000 + putEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER,175); //1.75 Multiplied by 100 } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index a5af0f8..c91458e 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -107,7 +107,37 @@ const static uint16_t OVERRIDE_GCODE_TEMP = 0x00C5; //4 Profiles = 0x00C6 + PROFILE_NEXT_OFFSET * 4 const static uint16_t PROFILE_BASE = 0x00C6; -//Next free location: 0x126 +//1 = Accelerated Stepper Driver, 0 = Regular stepper driver (default) +//Bit 2 is planner enabled +const static uint16_t STEPPER_DRIVER = 0x0126; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_MAX_FEEDRATE_X = 0x0127; +const static uint16_t ACCEL_MAX_FEEDRATE_Y = 0x012B; +const static uint16_t ACCEL_MAX_FEEDRATE_Z = 0x012F; +const static uint16_t ACCEL_MAX_FEEDRATE_A = 0x0133; +const static uint16_t ACCEL_MAX_FEEDRATE_B = 0x0137; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_MAX_ACCELERATION_X = 0x013B; +const static uint16_t ACCEL_MAX_ACCELERATION_Y = 0x013F; +const static uint16_t ACCEL_MAX_ACCELERATION_Z = 0x0143; +const static uint16_t ACCEL_MAX_ACCELERATION_A = 0x0147; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_MAX_EXTRUDER_NORM = 0x014B; +const static uint16_t ACCEL_MAX_EXTRUDER_RETRACT= 0x014F; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_E_STEPS_PER_MM = 0x0153; + +//uint32_t (4 bytes) +const static uint16_t ACCEL_MIN_FEED_RATE = 0x0157; +const static uint16_t ACCEL_MIN_TRAVEL_FEED_RATE= 0x015B; +const static uint16_t ACCEL_MAX_XY_JERK = 0x015F; +const static uint16_t ACCEL_MAX_Z_JERK = 0x0163; +const static uint16_t ACCEL_ADVANCE_K = 0x0167; +const static uint16_t ACCEL_FILAMENT_DIAMETER = 0x016B; /// Reset all data in the EEPROM to a default. void setDefaults(); diff --git a/firmware/src/Motherboard/Main.cc b/firmware/src/Motherboard/Main.cc index 4bd6485..78e86bc 100644 --- a/firmware/src/Motherboard/Main.cc +++ b/firmware/src/Motherboard/Main.cc @@ -43,6 +43,7 @@ void reset(bool hard_reset) { Motherboard& board = Motherboard::getBoard(); sdcard::reset(); steppers::abort(); + steppers::reset(); command::reset(); eeprom::init(); board.reset(hard_reset); diff --git a/firmware/src/Motherboard/Steppers.cc b/firmware/src/Motherboard/Steppers.cc index f6212d4..a3155c5 100644 --- a/firmware/src/Motherboard/Steppers.cc +++ b/firmware/src/Motherboard/Steppers.cc @@ -19,6 +19,15 @@ #include "Steppers.hh" #include "StepperAxis.hh" #include +#include +#include +#include +#include "EepromMap.hh" +#include "Eeprom.hh" + +#ifdef HAS_STEPPER_ACCELERATION +#include "StepperAccel.hh" +#endif namespace steppers { @@ -29,6 +38,15 @@ volatile int32_t intervals_remaining; StepperAxis axes[STEPPER_COUNT]; volatile bool is_homing; +#ifdef HAS_STEPPER_ACCELERATION + bool acceleration = false; + bool planner = false; + uint8_t plannerMaxBufferSize; + volatile bool force_acceleration_off = false; + + Point lastTarget; +#endif + bool holdZ = false; bool isRunning() { @@ -45,26 +63,140 @@ void init(Motherboard& motherboard) { for (int i = 0; i < STEPPER_COUNT; i++) { axes[i] = StepperAxis(motherboard.getStepperInterface(i)); } + + reset(); } void abort() { +#ifdef HAS_STEPPER_ACCELERATION + if ( acceleration ) quickStop(); +#endif is_running = false; is_homing = false; } +float convertAxisMMToFloat(int64_t st ) { + float aspmf = (float)st; + for (uint8_t i=0 ; i < STEPS_PER_MM_PRECISION; i ++ ) + aspmf /= 10.0; + return aspmf; +} + +void reset() { +#ifdef HAS_STEPPER_ACCELERATION + //Get the acceleration settings + uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + + acceleration = accel & 0x01; + planner = (accel & 0x02)?true:false; + + if ( acceleration ) { + //Good description of the settings can be found here: + //http://wiki.ultimaker.com/Marlin_firmware_for_the_Ultimaker + //and here: https://github.com/ErikZalm/Marlin + + //Here's more documentation on the various settings / features + //http://wiki.ultimaker.com/Marlin_firmware_for_the_Ultimaker + //https://github.com/ErikZalm/Marlin/commits/Marlin_v1 + //http://forums.reprap.org/read.php?147,94689,94689 + //http://reprap.org/pipermail/reprap-dev/2011-May/003323.html + //http://www.brokentoaster.com/blog/?p=358 + + //Same as axis steps:mm in the firmware + //Temporarily placed here, should be read from eeprom + axis_steps_per_unit[X_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_X, STEPS_PER_MM_X_DEFAULT)); + axis_steps_per_unit[Y_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Y, STEPS_PER_MM_Y_DEFAULT)); + axis_steps_per_unit[Z_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Z, STEPS_PER_MM_Z_DEFAULT)); + axis_steps_per_unit[E_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,44) / 10.0; + + //M201 - Set max acceleration in units/s^2 for print moves + // X, Y, Z, E maximum start speed for accelerated moves. E default values are good for skeinforge 40+, for older versions raise them a lot. + max_acceleration_units_per_sq_second[X_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, 2000); + max_acceleration_units_per_sq_second[Y_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, 2000); + max_acceleration_units_per_sq_second[Z_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, 150); + max_acceleration_units_per_sq_second[E_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, 60000); + + for (uint8_t i = 0; i < NUM_AXIS; i ++) + axis_steps_per_sqr_second[i] = max_acceleration_units_per_sq_second[i] * axis_steps_per_unit[i]; + + //M203 - Set maximum feedrate that your machine can sustain in mm/sec + max_feedrate[X_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, 160); + max_feedrate[Y_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, 160); + max_feedrate[Z_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, 10); + max_feedrate[E_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, 100); + + //M204 - Set default accelerationm for "Normal Moves (acceleration)" and "filament only moves (retraction)" in mm/sec^2 + + // X, Y, Z and E max acceleration in mm/s^2 for printing moves + p_acceleration = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, 5000); + + // X, Y, Z and E max acceleration in mm/s^2 for r retracts + p_retract_acceleration = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, 3000); + + //M205 - Advanced Settings + //minimumfeedrate - minimum travel speed while printing + minimumfeedrate = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,0) / 10.0; + + //mintravelfeedrate - minimum travel speed while travelling + mintravelfeedrate = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,0) / 10.0; + + //max_xy_jerk - maximum xy jerk (mm/sec) + max_xy_jerk = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_XY_JERK,2) / 10.0; + max_xy_jerk_squared = max_xy_jerk * max_xy_jerk; + + //max_z_jerk - maximum z jerk (mm/sec) + max_z_jerk = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_Z_JERK,100) / 10.0; + + float advanceK = (float)eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K,50) / 100000.0; + float filamentDiameter = (float)eeprom::getEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER,175) / 100.0; + + force_acceleration_off = false; + + if ( planner ) plannerMaxBufferSize = BLOCK_BUFFER_SIZE - 1; + else plannerMaxBufferSize = 1; + + plan_init(advanceK, filamentDiameter, axis_steps_per_unit[E_AXIS]); //Initialize planner + st_init(); //Initialize stepper + + lastTarget = Point(st_get_position(X_AXIS), st_get_position(Y_AXIS), st_get_position(Z_AXIS), st_get_position(E_AXIS), 0); + } + else lastTarget = getPosition(); +#endif +} + /// Define current position as given point void definePosition(const Point& position) { - for (int i = 0; i < STEPPER_COUNT; i++) { - axes[i].definePosition(position[i]); +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + plan_set_position(position[0], position[1], position[2], position[3]); } + else { +#endif + for (int i = 0; i < STEPPER_COUNT; i++) { + axes[i].definePosition(position[i]); + } +#ifdef HAS_STEPPER_ACCELERATION + } + lastTarget = position; +#endif } /// Get current position const Point getPosition() { #if STEPPER_COUNT > 3 - return Point(axes[0].position,axes[1].position,axes[2].position,axes[3].position,axes[4].position); +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) + return lastTarget; + else +#endif + return Point(axes[0].position,axes[1].position,axes[2].position,axes[3].position,axes[4].position); #else - return Point(axes[0].position,axes[1].position,axes[2].position); +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) + return Point(lastTarget[0], lastTarget[1], lastTarget[2]); + else +#endif + return Point(axes[0].position,axes[1].position,axes[2].position); #endif } @@ -72,51 +204,175 @@ void setHoldZ(bool holdZ_in) { holdZ = holdZ_in; } +#ifdef HAS_STEPPER_ACCELERATION + +//Calculates the feedrate based on moving from "from" to "to" + +float calcFeedRate(const Point& from, const Point& to, int32_t interval ) { + + //Calculate the distance in mm's by: + //Calculate the delta distances and convert to mm's + //Then sqr of sum of squares of the deltas for the distance + + //We also calculate at the same time, master_steps in steps by finding the dominant axis (for all 5) + //You would think it would be for X/Y/Z only, but they "mistakenly" use all 5 in rep g. + //so we do the same here for consistancy + + int32_t master_steps = 0; + float distance = 0.0; + + for (uint8_t i = 0; i < AXIS_COUNT; i ++ ) { + int32_t delta = to[i] - from[i]; + if ( delta < 0 ) delta *= -1; + + if ( delta > master_steps ) master_steps = delta; + + const float delta_mm = (float)delta / axis_steps_per_unit[i]; + distance += delta_mm * delta_mm; + } + distance = sqrt(distance); + + return (distance * 60000000.0) / ((float)interval * (float)master_steps); +} + +#endif + void setTarget(const Point& target, int32_t dda_interval) { - int32_t max_delta = 0; - for (int i = 0; i < AXIS_COUNT; i++) { - axes[i].setTarget(target[i], false); - const int32_t delta = axes[i].delta; - // Only shut z axis on inactivity - if (i == 2 && !holdZ) axes[i].enableStepper(delta != 0); - else if (delta != 0) axes[i].enableStepper(true); - if (delta > max_delta) { - max_delta = delta; +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + float feedRate = calcFeedRate(lastTarget, target, dda_interval ); + + //Figure out the distance between x1,y1 and x2,y2 using pythagoras + plan_buffer_line(target[0], target[1], target[2], target[3], feedRate, 0); + } else { +#endif + int32_t max_delta = 0; + for (int i = 0; i < AXIS_COUNT; i++) { + axes[i].setTarget(target[i], false); + const int32_t delta = axes[i].delta; + // Only shut z axis on inactivity + if (i == 2 && !holdZ) axes[i].enableStepper(delta != 0); + else if (delta != 0) axes[i].enableStepper(true); + if (delta > max_delta) { + max_delta = delta; + } } + // compute number of intervals for this move + intervals = ((max_delta * dda_interval) / INTERVAL_IN_MICROSECONDS); + intervals_remaining = intervals; + const int32_t negative_half_interval = -intervals / 2; + for (int i = 0; i < AXIS_COUNT; i++) { + axes[i].counter = negative_half_interval; + } + is_running = true; + +#ifdef HAS_STEPPER_ACCELERATION } - // compute number of intervals for this move - intervals = ((max_delta * dda_interval) / INTERVAL_IN_MICROSECONDS); - intervals_remaining = intervals; - const int32_t negative_half_interval = -intervals / 2; - for (int i = 0; i < AXIS_COUNT; i++) { - axes[i].counter = negative_half_interval; - } - is_running = true; + + lastTarget = target; +#endif } void setTargetNew(const Point& target, int32_t us, uint8_t relative) { - for (int i = 0; i < AXIS_COUNT; i++) { - axes[i].setTarget(target[i], (relative & (1 << i)) != 0); - // Only shut z axis on inactivity - const int32_t delta = axes[i].delta; - if (i == 2 && !holdZ) { - axes[i].enableStepper(delta != 0); - } else if (delta != 0) { - axes[i].enableStepper(true); +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + Point newPosition = target; + for (uint8_t i = 0; i < AXIS_COUNT; i ++) { + if ((relative & (1 << i)) != 0) + newPosition[i] = lastTarget[i] + target[i]; + } + + int32_t max_delta = 0; + for (int i = 0; i < AXIS_COUNT; i++) { + int32_t delta = newPosition[i] - lastTarget[i]; + if ( delta < 0 ) delta *= -1; + if (delta > max_delta) { + max_delta = delta; + } + } + int32_t dda_interval = us / max_delta; + float feedRate = calcFeedRate(lastTarget, newPosition, dda_interval); + + plan_buffer_line(newPosition[0], newPosition[1], newPosition[2], newPosition[3], feedRate, 0); + lastTarget = newPosition; + } else { +#endif + for (int i = 0; i < AXIS_COUNT; i++) { + axes[i].setTarget(target[i], (relative & (1 << i)) != 0); + // Only shut z axis on inactivity + const int32_t delta = axes[i].delta; + if (i == 2 && !holdZ) { + axes[i].enableStepper(delta != 0); + } else if (delta != 0) { + axes[i].enableStepper(true); + } + +#ifdef HAS_STEPPER_ACCELERATION + lastTarget[i] = axes[i].absoluteTarget; +#endif + } + // compute number of intervals for this move + intervals = us / INTERVAL_IN_MICROSECONDS; + intervals_remaining = intervals; + const int32_t negative_half_interval = -intervals / 2; + for (int i = 0; i < AXIS_COUNT; i++) { + axes[i].counter = negative_half_interval; } + is_running = true; + +#ifdef HAS_STEPPER_ACCELERATION } - // compute number of intervals for this move - intervals = us / INTERVAL_IN_MICROSECONDS; - intervals_remaining = intervals; - const int32_t negative_half_interval = -intervals / 2; - for (int i = 0; i < AXIS_COUNT; i++) { - axes[i].counter = negative_half_interval; +#endif +} + +#ifdef HAS_STEPPER_ACCELERATION +void switchToRegularDriver() { + if ( ! acceleration ) return; + + cli(); + + //Get the current position of the accelerated driver and + //store it in the regular driver + Point currentPosition = getPosition(); + for (int i = 0; i < STEPPER_COUNT; i++) { + axes[i].definePosition(currentPosition[i]); } - is_running = true; + + force_acceleration_off = true; + + //Change the interrupt frequency to the regular driver + Motherboard::getBoard().setupFixedStepperTimer(); + + sei(); } +void switchToAcceleratedDriver() { + if ( ! acceleration ) return; + + //Get the current position of the regular driver and + //store it in the accelerated driver + + cli(); + + force_acceleration_off = false; + + Point currentPosition = Point(axes[0].position, axes[1].position, axes[2].position, axes[3].position, axes[4].position); + definePosition(currentPosition); + + //Change the interrupt frequency to the accelerated driver + Motherboard::getBoard().setupAccelStepperTimer(); + + sei(); +} +#endif + /// Start homing void startHoming(const bool maximums, const uint8_t axes_enabled, const uint32_t us_per_step) { + +#ifdef HAS_STEPPER_ACCELERATION + if ( acceleration ) switchToRegularDriver(); +#endif + intervals_remaining = INT32_MAX; intervals = us_per_step / INTERVAL_IN_MICROSECONDS; const int32_t negative_half_interval = -intervals / 2; @@ -160,26 +416,63 @@ bool isAtMinimum(uint8_t index) { return false; } +void doLcd() { +// Motherboard::getBoard().lcd.setCursor(0,3); +// Motherboard::getBoard().lcd.writeFloat((float)movesplanned(),1); +// Motherboard::getBoard().lcd.write(' '); +} bool doInterrupt() { - if (is_running) { - if (intervals_remaining-- == 0) { - is_running = false; - } else { +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + //st_interrupt runs all the time, is_running reflects if we have space in the buffer or not, + //i.e. it enables us to add more commands if it's false + + if ( movesplanned() >= plannerMaxBufferSize) is_running = true; + else is_running = false; + + st_interrupt(); + + return is_running; + } else { +#endif + if (is_running) { + if (intervals_remaining-- == 0) { + is_running = false; + } else { + for (int i = 0; i < STEPPER_COUNT; i++) { + axes[i].doInterrupt(intervals); + } + } + return is_running; + } else if (is_homing) { + is_homing = false; for (int i = 0; i < STEPPER_COUNT; i++) { - axes[i].doInterrupt(intervals); + bool still_homing = axes[i].doHoming(intervals); + is_homing = still_homing || is_homing; } + +#ifdef HAS_STEPPER_ACCELERATION + //If homing has finished and we're accelerated, switch back to the accelerated drived + if (( ! is_homing ) && ( acceleration )) switchToAcceleratedDriver(); +#endif + + return is_homing; } - return is_running; - } else if (is_homing) { - is_homing = false; - for (int i = 0; i < STEPPER_COUNT; i++) { - bool still_homing = axes[i].doHoming(intervals); - is_homing = still_homing || is_homing; - } - return is_homing; +#ifdef HAS_STEPPER_ACCELERATION } +#endif return false; } +#ifdef HAS_STEPPER_ACCELERATION + +bool doAdvanceInterrupt() { +#ifdef ADVANCE + if ( acceleration ) st_advance_interrupt(); +#endif +} + +#endif + } diff --git a/firmware/src/Motherboard/Steppers.hh b/firmware/src/Motherboard/Steppers.hh index e8ede59..d6c3b1d 100644 --- a/firmware/src/Motherboard/Steppers.hh +++ b/firmware/src/Motherboard/Steppers.hh @@ -33,6 +33,9 @@ namespace steppers { /// \param[in] motherboard Motherboard to attach the steppers to. void init(Motherboard& motherboard); + //Reset the stepper subsystem + void reset(); + /// Check if the stepper subsystem is running /// \return True if the stepper subsystem is running or paused. False /// otherwise. @@ -84,10 +87,22 @@ namespace steppers { /// \param[in] position New system position void definePosition(const Point& position); + /// Switch to the regular driver + void switchToRegularDriver(); + + ///Switch to the accelerated driver + void switchToAcceleratedDriver(); + +//Debugging +void doLcd(); + /// Handle interrupt. /// \return True if the stepper subsystem is currently in motion. bool doInterrupt(); + //Used for the ADVANCE algorithm in Marlin + bool doAdvanceInterrupt(); + /// Get the current system position /// \return The current machine position. const Point getPosition(); diff --git a/firmware/src/Motherboard/boards/mb24/Configuration.hh b/firmware/src/Motherboard/boards/mb24/Configuration.hh index 2588aad..ced7ede 100644 --- a/firmware/src/Motherboard/boards/mb24/Configuration.hh +++ b/firmware/src/Motherboard/boards/mb24/Configuration.hh @@ -193,4 +193,7 @@ #define HAS_ATX_POWER_GOOD 1 #define ATX_POWER_GOOD Pin(PortK,2) //Pin ATX 8 connected to Analog 10 +//Stepper Acceleration +#define HAS_STEPPER_ACCELERATION 1 + #endif // BOARDS_RRMBV12_CONFIGURATION_HH_ diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index 33138dd..f991ada 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -94,6 +94,31 @@ Motherboard::Motherboard() : #endif } +void Motherboard::setupFixedStepperTimer() { + TCCR1A = 0x00; + TCCR1B = 0x09; + TCCR1C = 0x00; + OCR1A = INTERVAL_IN_MICROSECONDS * 16; + TIMSK1 = 0x02; // turn on OCR1A match interrupt +} + +void Motherboard::setupAccelStepperTimer() { + // waveform generation = 0100 = CTC + TCCR1B &= ~(1< 1000000L) { seconds += 1; countupMicros -= 1000000L; } - - steppers::doInterrupt(); } void Motherboard::runMotherboardSlice() { @@ -233,7 +281,17 @@ MoodLightController Motherboard::getMoodLightController() { /// Timer one comparator match interrupt ISR(TIMER1_COMPA_vect) { - Motherboard::getBoard().doInterrupt(); + Motherboard::getBoard().doStepperInterrupt(); +} + +/// Timer one comparator match interrupt +ISR(TIMER3_COMPA_vect) { + Motherboard::getBoard().doInterfaceInterrupt(); +} + +/// Timer one comparator match interrupt +ISR(TIMER4_COMPA_vect) { + Motherboard::getBoard().doAdvanceInterrupt(); } /// Number of times to blink the debug LED on each cycle diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.hh b/firmware/src/Motherboard/boards/mb24/Motherboard.hh index 459422b..8f48c4a 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.hh +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.hh @@ -95,6 +95,10 @@ private: void serviceBuzzer(); public: + //2 types of stepper timers depending on if we're using accelerated or not + void setupFixedStepperTimer(); + void setupAccelStepperTimer(); + /// Reset the motherboard to its initial state. /// This only resets the board, and does not send a reset /// to any attached toolheads. @@ -110,6 +114,11 @@ public: return stepper[n]; } + StepperInterface *getStepperAllInterfaces() + { + return stepper; + } + /// Get the number of microseconds that have passed since /// the board was initialized. This value will wrap after /// 2**32 microseconds (ca. 70 minutes); callers should compensate for this. @@ -122,8 +131,13 @@ public: /// Get the current error being displayed. uint8_t getCurrentError(); - /// Perform the timer interrupt routine. - void doInterrupt(); + /// Perform the stepper timer interrupt routine. + void doStepperInterrupt(); + + void doAdvanceInterrupt(); + + /// Perform the interface timer interrupt routine. + void doInterfaceInterrupt(); MoodLightController getMoodLightController(); void MoodLightSetRGBColor(uint8_t r, uint8_t g, uint8_t b, uint8_t fadeSpeed, uint8_t writeToEeprom); diff --git a/firmware/src/shared/CircularBuffer.hh b/firmware/src/shared/CircularBuffer.hh index a27cd7b..e1009cb 100644 --- a/firmware/src/shared/CircularBuffer.hh +++ b/firmware/src/shared/CircularBuffer.hh @@ -74,6 +74,16 @@ public: return popped_byte; } + /// Peek at a byte at the head of the buffer + inline BufDataType peek() { + if (isEmpty()) { + underflow = true; + return BufDataType(); + } + const BufDataType& popped_byte = operator[](0); + return popped_byte; + } + /// Pop a number of bytes off the head of the buffer. If there /// are not enough bytes to complete the pop, pop what we can and /// set the underflow flag. diff --git a/firmware/src/shared/Eeprom.cc b/firmware/src/shared/Eeprom.cc index ba6b7ca..cbb4a65 100644 --- a/firmware/src/shared/Eeprom.cc +++ b/firmware/src/shared/Eeprom.cc @@ -51,10 +51,39 @@ int64_t getEepromInt64(const uint16_t location, const int64_t default_value) { return *ret; } +uint32_t getEepromUInt32(const uint16_t location, const uint32_t default_value) { + uint32_t *ret; + uint8_t data[4]; + eeprom_read_block(data,(const uint8_t*)location,4); + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff && data[3] == 0xff) + return default_value; + ret = (uint32_t *)&data[0]; + return *ret; +} + void putEepromInt64(const uint16_t location, const int64_t value) { void *data; data = (void *)&value; eeprom_write_block(data,(void*)location,8); } +void putEepromUInt32(const uint16_t location, const uint32_t value) { + void *data; + data = (void *)&value; + eeprom_write_block(data,(void*)location,4); +} + +int64_t getEepromStepsPerMM(const uint16_t location, const int64_t default_value) { + int64_t value = eeprom::getEepromInt64(location, default_value); + + if (( value <= STEPS_PER_MM_LOWER_LIMIT ) || ( value >= STEPS_PER_MM_UPPER_LIMIT )) { + eeprom::putEepromInt64(location, default_value); + + //Just to be on the safe side + value = eeprom::getEepromInt64(location, default_value); + } + + return value; +} + } // namespace eeprom diff --git a/firmware/src/shared/Eeprom.hh b/firmware/src/shared/Eeprom.hh index 668ef2e..c3c54cb 100644 --- a/firmware/src/shared/Eeprom.hh +++ b/firmware/src/shared/Eeprom.hh @@ -11,8 +11,10 @@ uint8_t getEeprom8(const uint16_t location, const uint8_t default_value); uint16_t getEeprom16(const uint16_t location, const uint16_t default_value); float getEepromFixed16(const uint16_t location, const float default_value); int64_t getEepromInt64(const uint16_t location, const int64_t default_value); +uint32_t getEepromUInt32(const uint16_t location, const uint32_t default_value); void putEepromInt64(const uint16_t location, const int64_t value); - +void putEepromUInt32(const uint16_t location, const uint32_t value); +int64_t getEepromStepsPerMM(const uint16_t location, const int64_t default_value); } #define STEPS_PER_MM_PADDING 5 diff --git a/firmware/src/shared/InterfaceBoard.hh b/firmware/src/shared/InterfaceBoard.hh index eefa9b1..86d4adc 100644 --- a/firmware/src/shared/InterfaceBoard.hh +++ b/firmware/src/shared/InterfaceBoard.hh @@ -26,7 +26,7 @@ #include "MoodLightController.hh" /// Maximum number of screens that can be active at once. -#define SCREEN_STACK_DEPTH 5 +#define SCREEN_STACK_DEPTH 7 /// Character LCD screen geometry /// diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index fe33c4f..6df37ad 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -50,6 +50,61 @@ enum Axis { }; +//Stack checking +//http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=52249 +extern uint8_t _end; +extern uint8_t __stack; + +#define STACK_CANARY 0xc5 + +void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1"))); + +void StackPaint(void) +{ +#if 0 + uint8_t *p = &_end; + + while(p <= &__stack) + { + *p = STACK_CANARY; + p++; + } +#else + __asm volatile (" ldi r30,lo8(_end)\n" + " ldi r31,hi8(_end)\n" + " ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */ + " ldi r25,hi8(__stack)\n" + " rjmp .cmp\n" + ".loop:\n" + " st Z+,r24\n" + ".cmp:\n" + " cpi r30,lo8(__stack)\n" + " cpc r31,r25\n" + " brlo .loop\n" + " breq .loop"::); +#endif +} + + +uint16_t StackCount(void) +{ + const uint8_t *p = &_end; + uint16_t c = 0; + + while(*p == STACK_CANARY && p <= &__stack) + { + p++; + c++; + } + + return c; +} + + +void VersionMode::reset() { +} + + //Convert mm's to steps for the given axis //Accurate to 1/1000 mm @@ -236,10 +291,10 @@ int appendUint8(char *buf, uint8_t buflen, uint8_t val) void SplashScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar splash1[] = " "; - const static PROGMEM prog_uchar splash2[] = " Thing-O-Matic "; - const static PROGMEM prog_uchar splash3[] = " --------- "; - const static PROGMEM prog_uchar splash4[] = " "; + const static PROGMEM prog_uchar splash1[] = " Thing-O-Matic "; + const static PROGMEM prog_uchar splash2[] = " --------- "; + const static PROGMEM prog_uchar splash3[] = " Jetty Firmware "; + const static PROGMEM prog_uchar splash4[] = "Thing 15380 3.0b"; if (forceRedraw) { @@ -330,6 +385,8 @@ void JogMode::reset() { userViewMode = jogModeSettings & 0x01; userViewModeChanged = false; + + steppers::switchToRegularDriver(); } void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { @@ -483,6 +540,7 @@ void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { steppers::enableAxis(1, false); steppers::enableAxis(2, false); interface::popScreen(); + steppers::switchToAcceleratedDriver(); break; } } @@ -493,6 +551,7 @@ void ExtruderMode::reset() { timeChanged = false; lastDirection = 1; overrideExtrudeSeconds = 0; + steppers::switchToRegularDriver(); } void ExtruderMode::update(LiquidCrystal& lcd, bool forceRedraw) { @@ -666,6 +725,7 @@ void ExtruderMode::notifyButtonPressed(ButtonArray::ButtonName button) { steppers::abort(); steppers::enableAxis(3, false); interface::popScreen(); + steppers::switchToAcceleratedDriver(); break; } } @@ -1438,6 +1498,8 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { updatePhase = (enum UpdatePhase)((uint8_t)updatePhase + 1); if (updatePhase >= UPDATE_PHASE_LAST) updatePhase = UPDATE_PHASE_FIRST; + + steppers::doLcd(); } void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { @@ -1470,14 +1532,12 @@ void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } -void VersionMode::reset() { -} + void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar version1[] = "Firmware Version"; - const static PROGMEM prog_uchar version2[] = "----------------"; - const static PROGMEM prog_uchar version3[] = "Motherboard: _._"; - const static PROGMEM prog_uchar version4[] = " Extruder: _._"; + const static PROGMEM prog_uchar version1[] = "Motherboard: _._"; + const static PROGMEM prog_uchar version2[] = " Extruder: _._"; + const static PROGMEM prog_uchar version4[] = "FreeSram: "; if (forceRedraw) { lcd.clear(); @@ -1488,17 +1548,11 @@ void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,1); lcd.writeFromPgmspace(version2); - lcd.setCursor(0,2); - lcd.writeFromPgmspace(version3); - - lcd.setCursor(0,3); - lcd.writeFromPgmspace(version4); - //Display the motherboard version - lcd.setCursor(13, 2); + lcd.setCursor(13, 0); lcd.writeInt(firmware_version / 100, 1); - lcd.setCursor(15, 2); + lcd.setCursor(15, 0); lcd.writeInt(firmware_version % 100, 1); //Display the extruder version @@ -1507,15 +1561,19 @@ void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { if (extruderControl(SLAVE_CMD_VERSION, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t extruderVersion = responsePacket.read16(1); - lcd.setCursor(13, 3); + lcd.setCursor(13, 1); lcd.writeInt(extruderVersion / 100, 1); - lcd.setCursor(15, 3); + lcd.setCursor(15, 1); lcd.writeInt(extruderVersion % 100, 1); } else { - lcd.setCursor(13, 3); + lcd.setCursor(13, 1); lcd.writeString("X.X"); } + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(version4); + lcd.writeFloat((float)StackCount(),0); } else { } } @@ -1742,30 +1800,17 @@ void CancelBuildMenu::handleSelect(uint8_t index) { lind ++; } -int64_t MainMenu::checkAndGetEepromDefault(const uint16_t location, const int64_t default_value) { - int64_t value = eeprom::getEepromInt64(location, default_value); - - if (( value <= STEPS_PER_MM_LOWER_LIMIT ) || ( value >= STEPS_PER_MM_UPPER_LIMIT )) { - eeprom::putEepromInt64(location, default_value); - - //Just to be on the safe side - value = eeprom::getEepromInt64(location, default_value); - } - - return value; -} - MainMenu::MainMenu() { itemCount = 21; reset(); //Read in the axisStepsPerMM, we'll need these for various firmware functions later on cli(); - axisStepsPerMM[AXIS_X] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_X, STEPS_PER_MM_X_DEFAULT); - axisStepsPerMM[AXIS_Y] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_Y, STEPS_PER_MM_Y_DEFAULT); - axisStepsPerMM[AXIS_Z] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_Z, STEPS_PER_MM_Z_DEFAULT); - axisStepsPerMM[AXIS_A] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_A, STEPS_PER_MM_A_DEFAULT); - axisStepsPerMM[AXIS_B] = checkAndGetEepromDefault(eeprom::STEPS_PER_MM_B, STEPS_PER_MM_B_DEFAULT); + axisStepsPerMM[AXIS_X] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_X, STEPS_PER_MM_X_DEFAULT); + axisStepsPerMM[AXIS_Y] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Y, STEPS_PER_MM_Y_DEFAULT); + axisStepsPerMM[AXIS_Z] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Z, STEPS_PER_MM_Z_DEFAULT); + axisStepsPerMM[AXIS_A] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_A, STEPS_PER_MM_A_DEFAULT); + axisStepsPerMM[AXIS_B] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_B, STEPS_PER_MM_B_DEFAULT); sei(); } @@ -2599,6 +2644,8 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { case 0: //Entered pause, waiting for steppers to finish last command lcd.writeFromPgmspace(waitForCurrentCommand); + steppers::switchToRegularDriver(); + if ( ! steppers::isRunning()) pauseState ++; break; @@ -2704,6 +2751,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { interface::popScreen(); command::pause(false); if ( ! autoPause ) interface::popScreen(); + steppers::switchToAcceleratedDriver(); } break; } @@ -3490,7 +3538,7 @@ void FilamentUsedMode::notifyButtonPressed(ButtonArray::ButtonName button) { } BuildSettingsMenu::BuildSettingsMenu() { - itemCount = 3; + itemCount = 4; reset(); } @@ -3503,6 +3551,7 @@ void BuildSettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar item1[] = "EstimatePreheat"; const static PROGMEM prog_uchar item2[] = "Override Temp"; const static PROGMEM prog_uchar item3[] = "ABP Copies (SD)"; + const static PROGMEM prog_uchar item4[] = "Acceleration"; switch (index) { case 0: @@ -3514,6 +3563,9 @@ void BuildSettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { case 2: lcd.writeFromPgmspace(item3); break; + case 3: + lcd.writeFromPgmspace(item4); + break; } } @@ -3533,6 +3585,10 @@ void BuildSettingsMenu::handleSelect(uint8_t index) { //Change number of ABP copies interface::pushScreen(&abpCopiesSetScreen); break; + case 3: + //Acceleration menu + interface::pushScreen(&accelerationMenu); + break; } } @@ -3698,6 +3754,74 @@ void OverrideGCodeTempMenu::handleSelect(uint8_t index) { } } +StepperDriverAcceleratedMenu::StepperDriverAcceleratedMenu() { + itemCount = 5; + reset(); +} + +void StepperDriverAcceleratedMenu::resetState() { + uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + if ( accel == 0x03 ) itemIndex = 4; + else if ( accel == 0x01 ) itemIndex = 3; + else itemIndex = 2; + firstItemIndex = 2; +} + +void StepperDriverAcceleratedMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg1[] = "Accelerated"; + const static PROGMEM prog_uchar msg2[] = "Stepper Driver:"; + const static PROGMEM prog_uchar off[] = "Off"; + const static PROGMEM prog_uchar on[] = "On - No Planner"; + const static PROGMEM prog_uchar planner[]= "On - Planner"; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg1); + break; + case 1: + lcd.writeFromPgmspace(msg2); + break; + case 2: + lcd.writeFromPgmspace(off); + break; + case 3: + lcd.writeFromPgmspace(on); + break; + case 4: + lcd.writeFromPgmspace(planner); + break; + } +} + +void StepperDriverAcceleratedMenu::handleSelect(uint8_t index) { + uint8_t oldValue = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + uint8_t newValue = oldValue; + + switch (index) { + case 2: + newValue = 0x00; + interface::popScreen(); + break; + case 3: + newValue = 0x01; + interface::popScreen(); + break; + case 4: + newValue = 0x03; + interface::popScreen(); + break; + } + + //If the value has changed, do a reset + if ( newValue != oldValue ) { + cli(); + eeprom_write_byte((uint8_t*)eeprom::STEPPER_DRIVER, newValue); + sei(); + //Reset + host::stopBuild(); + } +} + #define NUM_PROFILES 4 #define PROFILES_SAVED_AXIS 3 @@ -4147,4 +4271,509 @@ void UnableToOpenFileMenu::handleSelect(uint8_t index) { interface::popScreen(); } +void AcceleratedSettingsMode::reset() { + cli(); + values[0] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, 160); + values[1] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, 160); + values[2] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, 10); + values[3] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, 100); + values[4] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, 2000); + values[5] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, 2000); + values[6] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, 150); + values[7] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, 60000); + values[8] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, 5000); + values[9] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, 3000); + values[10] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,0); + values[11] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,0); + values[12] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_XY_JERK,2); + values[13] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_Z_JERK,100); + values[14] = eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K,50); + values[15] = eeprom::getEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER,175); + sei(); + + lastAccelerateSettingsState= AS_NONE; + accelerateSettingsState= AS_MAX_FEEDRATE_X; +} + +void AcceleratedSettingsMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1xMaxFeedRate[] = "X MaxFeedRate:"; + const static PROGMEM prog_uchar message1yMaxFeedRate[] = "Y MaxFeedRate:"; + const static PROGMEM prog_uchar message1zMaxFeedRate[] = "Z MaxFeedRate:"; + const static PROGMEM prog_uchar message1aMaxFeedRate[] = "A MaxFeedRate:"; + const static PROGMEM prog_uchar message1xMaxAccelRate[] = "X Max Accel:"; + const static PROGMEM prog_uchar message1yMaxAccelRate[] = "Y Max Accel:"; + const static PROGMEM prog_uchar message1zMaxAccelRate[] = "Z Max Accel:"; + const static PROGMEM prog_uchar message1aMaxAccelRate[] = "A Max Accel:"; + const static PROGMEM prog_uchar message1ExtruderNorm[] = "Acc Norm Move:"; + const static PROGMEM prog_uchar message1ExtruderRetract[] = "Acc Extr Move:"; + const static PROGMEM prog_uchar message1MinFeedRate[] = "Min Feed Rate:"; + const static PROGMEM prog_uchar message1MinTravelFeedRate[] = "MinTrvlFeedRate:"; + const static PROGMEM prog_uchar message1MaxXYJerk[] = "Max XY Jerk:"; + const static PROGMEM prog_uchar message1MaxZJerk[] = "Max Z Jerk:"; + const static PROGMEM prog_uchar message1AdvanceK[] = "Advance K:"; + const static PROGMEM prog_uchar message1FilamentDiameter[] = "Filament Dia:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if ( accelerateSettingsState != lastAccelerateSettingsState ) forceRedraw = true; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + switch(accelerateSettingsState) { + case AS_MAX_FEEDRATE_X: + lcd.writeFromPgmspace(message1xMaxFeedRate); + break; + case AS_MAX_FEEDRATE_Y: + lcd.writeFromPgmspace(message1yMaxFeedRate); + break; + case AS_MAX_FEEDRATE_Z: + lcd.writeFromPgmspace(message1zMaxFeedRate); + break; + case AS_MAX_FEEDRATE_A: + lcd.writeFromPgmspace(message1aMaxFeedRate); + break; + case AS_MAX_ACCELERATION_X: + lcd.writeFromPgmspace(message1xMaxAccelRate); + break; + case AS_MAX_ACCELERATION_Y: + lcd.writeFromPgmspace(message1yMaxAccelRate); + break; + case AS_MAX_ACCELERATION_Z: + lcd.writeFromPgmspace(message1zMaxAccelRate); + break; + case AS_MAX_ACCELERATION_A: + lcd.writeFromPgmspace(message1aMaxAccelRate); + break; + case AS_MAX_EXTRUDER_NORM: + lcd.writeFromPgmspace(message1ExtruderNorm); + break; + case AS_MAX_EXTRUDER_RETRACT: + lcd.writeFromPgmspace(message1ExtruderRetract); + break; + case AS_MIN_FEED_RATE: + lcd.writeFromPgmspace(message1MinFeedRate); + break; + case AS_MIN_TRAVEL_FEED_RATE: + lcd.writeFromPgmspace(message1MinTravelFeedRate); + break; + case AS_MAX_XY_JERK: + lcd.writeFromPgmspace(message1MaxXYJerk); + break; + case AS_MAX_Z_JERK: + lcd.writeFromPgmspace(message1MaxZJerk); + break; + case AS_ADVANCE_K: + lcd.writeFromPgmspace(message1AdvanceK); + break; + case AS_FILAMENT_DIAMETER: + lcd.writeFromPgmspace(message1FilamentDiameter); + break; + } + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + uint32_t value = 0; + + uint8_t currentIndex = accelerateSettingsState - AS_MAX_FEEDRATE_X; + + value = values[currentIndex]; + + lcd.setCursor(0,1); + + switch(accelerateSettingsState) { + case AS_MIN_FEED_RATE: + case AS_MIN_TRAVEL_FEED_RATE: + case AS_MAX_XY_JERK: + case AS_MAX_Z_JERK: + lcd.writeFloat((float)value / 10.0, 1); + break; + case AS_ADVANCE_K: + lcd.writeFloat((float)value / 100000.0, 5); + break; + case AS_FILAMENT_DIAMETER: + lcd.writeFloat((float)value / 100.0, 2); + break; + default: + lcd.writeFloat((float)value, 0); + break; + } + + lcd.writeFromPgmspace(blank); + + lastAccelerateSettingsState = accelerateSettingsState; +} + +void AcceleratedSettingsMode::notifyButtonPressed(ButtonArray::ButtonName button) { + if (( accelerateSettingsState == AS_FILAMENT_DIAMETER ) && (button == ButtonArray::OK )) { + //Write the data + cli(); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, values[0]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, values[1]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, values[2]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, values[3]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, values[4]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, values[5]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, values[6]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, values[7]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, values[8]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, values[9]); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE, values[10]); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE, values[11]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_XY_JERK, values[12]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_Z_JERK, values[13]); + eeprom::putEepromUInt32(eeprom::ACCEL_ADVANCE_K, values[14]); + eeprom::putEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER, values[15]); + sei(); + + host::stopBuild(); + return; + } + + uint8_t currentIndex = accelerateSettingsState - AS_MAX_FEEDRATE_X; + + uint32_t lastValue = values[currentIndex]; + + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + accelerateSettingsState = (enum accelerateSettingsState)((uint8_t)accelerateSettingsState + 1); + return; + break; + case ButtonArray::ZPLUS: + // increment more + values[currentIndex] += 100; + break; + case ButtonArray::ZMINUS: + // decrement more + values[currentIndex] -= 100; + break; + case ButtonArray::YPLUS: + // increment less + values[currentIndex] += 1; + break; + case ButtonArray::YMINUS: + // decrement less + values[currentIndex] -= 1; + break; + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } + + if (!(( accelerateSettingsState == AS_MIN_FEED_RATE ) || ( accelerateSettingsState == AS_MIN_TRAVEL_FEED_RATE ) || ( accelerateSettingsState == AS_ADVANCE_K ))) { + if ( values[currentIndex] < 1 ) values[currentIndex] = 1; + } + + if ( values[currentIndex] > 200000 ) values[currentIndex] = 1; +} + +AccelerationMenu::AccelerationMenu() { + itemCount = 3; + + reset(); +} + +void AccelerationMenu::resetState() { + if ( eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0) ) acceleration = true; + else acceleration = false; + + if ( acceleration ) itemCount = 3; + else itemCount = 1; + + itemIndex = 0; + firstItemIndex = 0; +} + +void AccelerationMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar msg1[] = "Stepper Driver"; + const static PROGMEM prog_uchar msg2[] = "Accel. Settings"; + const static PROGMEM prog_uchar msg3[] = "Extdr. Steps/mm"; + + if (( ! acceleration ) && ( index > 0 )) return; + + switch (index) { + case 0: + lcd.writeFromPgmspace(msg1); + break; + case 1: + lcd.writeFromPgmspace(msg2); + break; + case 2: + lcd.writeFromPgmspace(msg3); + break; + } +} + +void AccelerationMenu::handleSelect(uint8_t index) { + if (( ! acceleration ) && ( index > 0 )) return; + + switch (index) { + case 0: + interface::pushScreen(&stepperDriverAcceleratedMenu); + break; + case 1: + interface::pushScreen(&acceleratedSettingsMode); + break; + case 2: + interface::pushScreen(&eStepsPerMMMode); + break; + } +} + +void EStepsPerMMMode::reset() { + value = eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, 44); + if ( value < 1 ) { + eeprom::putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, 44); + value = eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, 44); //Just in case + } +} + +void EStepsPerMMMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Extrdr Steps/mm:"; + const static PROGMEM prog_uchar message2[] = "(calib)"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(message2); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(8,2); + lcd.writeFloat((float)value / 10.0,1); + lcd.writeFromPgmspace(blank); +} + +void EStepsPerMMMode::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + eeprom::putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,value); + //Reset to read in the new value + host::stopBuild(); + return; + break; + case ButtonArray::ZPLUS: + // increment more + value += 25; + break; + case ButtonArray::ZMINUS: + // decrement more + value -= 25; + break; + case ButtonArray::YPLUS: + // increment less + value += 1; + break; + case ButtonArray::YMINUS: + // decrement less + value -= 1; + break; + + case ButtonArray::XMINUS: + interface::pushScreen(&eStepsPerMMStepsMode); + break; + case ButtonArray::XPLUS: + break; + } + + if (( value < 1 ) || ( value > 200000 )) value = 1; +} + +void EStepsPerMMStepsMode::reset() { + value = 200; + steppers::switchToRegularDriver(); + overrideExtrudeSeconds = 0; +} + +void EStepsPerMMStepsMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Extrude N steps:"; + const static PROGMEM prog_uchar message2[] = "(extrude)"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if (overrideExtrudeSeconds) extrude(true); + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,2); + lcd.writeFromPgmspace(message2); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(10,2); + lcd.writeFloat((float)value,0); + lcd.writeFromPgmspace(blank); +} + +void EStepsPerMMStepsMode::extrude(bool overrideTempCheck) { + //Check we're hot enough + if ( ! overrideTempCheck ) + { + OutPacket responsePacket; + if (extruderControl(SLAVE_CMD_IS_TOOL_READY, EXTDR_CMD_GET, responsePacket, 0)) { + uint8_t data = responsePacket.read8(1); + + if ( ! data ) + { + overrideExtrudeSeconds = 1; + interface::pushScreen(&extruderTooColdMenu); + return; + } + } + } + + Point position = steppers::getPosition(); + + float rpm = (float)eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19) / 10.0; + + //60 * 1000000 = # uS in a minute + //200 * 8 = 200 steps per revolution * 1/8 stepping + int32_t interval = (int32_t)(60L * 1000000L) / (int32_t)((float)(200 * 8) * rpm); + + position[3] += -value; + steppers::setTarget(position, interval); + + if (overrideTempCheck) overrideExtrudeSeconds = 0; +} + + +void EStepsPerMMStepsMode::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + steppers::switchToAcceleratedDriver(); + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + steppers::switchToAcceleratedDriver(); + eStepsPerMMLengthMode.steps = value; + interface::pushScreen(&eStepsPerMMLengthMode); + break; + case ButtonArray::ZPLUS: + // increment more + value += 25; + break; + case ButtonArray::ZMINUS: + // decrement more + value -= 25; + break; + case ButtonArray::YPLUS: + // increment less + value += 1; + break; + case ButtonArray::YMINUS: + // decrement less + value -= 1; + break; + + case ButtonArray::XMINUS: + extrude(false); + break; + case ButtonArray::XPLUS: + break; + } +} + +void EStepsPerMMLengthMode::reset() { + value = 1; +} + +void EStepsPerMMLengthMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "Enter noodle"; + const static PROGMEM prog_uchar message2[] = "length in mm's"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,1); + lcd.writeFromPgmspace(message2); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(0,2); + lcd.writeFloat((float)value,0); + lcd.writeFromPgmspace(blank); +} + +void EStepsPerMMLengthMode::notifyButtonPressed(ButtonArray::ButtonName button) { + uint32_t espm; + + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + if ( steps < 0 ) steps *= -1; + espm = (uint32_t)lround(((float)steps / (float)value) * 10.0); + eeprom::putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,espm); + + //Reset to read in the new value + host::stopBuild(); + return; + break; + case ButtonArray::ZPLUS: + // increment more + value += 5; + break; + case ButtonArray::ZMINUS: + // decrement more + value -= 5; + break; + case ButtonArray::YPLUS: + // increment less + value += 1; + break; + case ButtonArray::YMINUS: + // decrement less + value -= 1; + break; + + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } + + if (( value < 1 ) || ( value > 200000 )) value = 1; +} + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 39b5d37..41d7ec8 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -688,11 +688,125 @@ protected: void handleSelect(uint8_t index); }; +class StepperDriverAcceleratedMenu: public Menu { +public: + StepperDriverAcceleratedMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + +class AcceleratedSettingsMode: public Screen { +private: + enum accelerateSettingsState { + AS_NONE, + AS_MAX_FEEDRATE_X, + AS_MAX_FEEDRATE_Y, + AS_MAX_FEEDRATE_Z, + AS_MAX_FEEDRATE_A, + AS_MAX_ACCELERATION_X, + AS_MAX_ACCELERATION_Y, + AS_MAX_ACCELERATION_Z, + AS_MAX_ACCELERATION_A, + AS_MAX_EXTRUDER_NORM, + AS_MAX_EXTRUDER_RETRACT, + AS_MIN_FEED_RATE, + AS_MIN_TRAVEL_FEED_RATE, + AS_MAX_XY_JERK, + AS_MAX_Z_JERK, + AS_ADVANCE_K, + AS_FILAMENT_DIAMETER, + }; + + enum accelerateSettingsState accelerateSettingsState, lastAccelerateSettingsState; + + uint32_t values[16]; + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + +class EStepsPerMMLengthMode: public Screen { +private: + uint32_t value; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); + + int32_t steps; +}; + +class EStepsPerMMStepsMode: public Screen { +private: + int32_t value; + ExtruderTooColdMenu extruderTooColdMenu; + EStepsPerMMLengthMode eStepsPerMMLengthMode; + + void extrude(bool overrideTempCheck); + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + +class EStepsPerMMMode: public Screen { +private: + uint32_t value; + EStepsPerMMStepsMode eStepsPerMMStepsMode; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + +class AccelerationMenu: public Menu { +private: + StepperDriverAcceleratedMenu stepperDriverAcceleratedMenu; + AcceleratedSettingsMode acceleratedSettingsMode; + EStepsPerMMMode eStepsPerMMMode; + + bool acceleration; +public: + AccelerationMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +}; + class BuildSettingsMenu: public Menu { private: PreheatDuringEstimateMenu preheatDuringEstimateMenu; OverrideGCodeTempMenu overrideGCodeTempMenu; ABPCopiesSetScreen abpCopiesSetScreen; + AccelerationMenu accelerationMenu; public: BuildSettingsMenu(); diff --git a/firmware/src/shared/StepperAccel.cc b/firmware/src/shared/StepperAccel.cc new file mode 100644 index 0000000..e051612 --- /dev/null +++ b/firmware/src/shared/StepperAccel.cc @@ -0,0 +1,537 @@ +/* + StepperAccel.cc - stepper motor driver: executes motion plans using stepper motors + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + + Grbl 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, or + (at your option) any later version. + + Grbl 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 Grbl. If not, see . +*/ + +/* The timer calculations of this module informed by the 'RepRap cartesian firmware' by Zack Smith + and Philipp Tiefenbacher. */ + +#include "Configuration.hh" + +#ifdef HAS_STEPPER_ACCELERATION + + + +#include "StepperAccel.hh" +#include "StepperAccelSpeedTable.hh" +#include "StepperInterface.hh" +#include "Motherboard.hh" + +#include + + + + +//=========================================================================== +//=============================public variables ============================ +//=========================================================================== +block_t *current_block; // A pointer to the block currently being traced + + +//=========================================================================== +//=============================private variables ============================ +//=========================================================================== +//static makes it inpossible to be called from outside of this file by extern.! + +// Variables used by The Stepper Driver Interrupt +static unsigned char out_bits; // The next stepping-bits to be output +static int32_t counter_x, // Counter variables for the bresenham line tracer + counter_y, + counter_z, + counter_e; +volatile static uint32_t step_events_completed; // The number of step events executed in the current block +#ifdef ADVANCE + static int32_t advance_rate = 0, advance, final_advance = 0; + static int32_t old_advance = 0; +#endif +volatile static int32_t e_steps[3]; +volatile static unsigned char busy = false; // TRUE when SIG_OUTPUT_COMPARE1A is being serviced. Used to avoid retriggering that handler. +static int32_t acceleration_time, deceleration_time; +//static uint32_t accelerate_until, decelerate_after, acceleration_rate, initial_rate, final_rate, nominal_rate; +static unsigned short acc_step_rate; // needed for deccelaration start point +static char step_loops; +static unsigned short OCR1A_nominal; + +volatile int32_t count_position[NUM_AXIS] = { 0, 0, 0, 0}; +volatile char count_direction[NUM_AXIS] = { 1, 1, 1, 1}; + +static StepperInterface *stepperInterface; + +//=========================================================================== +//=============================functions ============================ +//=========================================================================== + +// intRes = intIn1 * intIn2 >> 16 +// uses: +// r26 to store 0 +// r27 to store the byte 1 of the 24 bit result +#define MultiU16X8toH16(intRes, charIn1, intIn2) \ +asm volatile ( \ +"clr r26 \n\t" \ +"mul %A1, %B2 \n\t" \ +"movw %A0, r0 \n\t" \ +"mul %A1, %A2 \n\t" \ +"add %A0, r1 \n\t" \ +"adc %B0, r26 \n\t" \ +"lsr r0 \n\t" \ +"adc %A0, r26 \n\t" \ +"adc %B0, r26 \n\t" \ +"clr r1 \n\t" \ +: \ +"=&r" (intRes) \ +: \ +"d" (charIn1), \ +"d" (intIn2) \ +: \ +"r26" \ +) + +// intRes = longIn1 * longIn2 >> 24 +// uses: +// r26 to store 0 +// r27 to store the byte 1 of the 48bit result +#define MultiU24X24toH16(intRes, longIn1, longIn2) \ +asm volatile ( \ +"clr r26 \n\t" \ +"mul %A1, %B2 \n\t" \ +"mov r27, r1 \n\t" \ +"mul %B1, %C2 \n\t" \ +"movw %A0, r0 \n\t" \ +"mul %C1, %C2 \n\t" \ +"add %B0, r0 \n\t" \ +"mul %C1, %B2 \n\t" \ +"add %A0, r0 \n\t" \ +"adc %B0, r1 \n\t" \ +"mul %A1, %C2 \n\t" \ +"add r27, r0 \n\t" \ +"adc %A0, r1 \n\t" \ +"adc %B0, r26 \n\t" \ +"mul %B1, %B2 \n\t" \ +"add r27, r0 \n\t" \ +"adc %A0, r1 \n\t" \ +"adc %B0, r26 \n\t" \ +"mul %C1, %A2 \n\t" \ +"add r27, r0 \n\t" \ +"adc %A0, r1 \n\t" \ +"adc %B0, r26 \n\t" \ +"mul %B1, %A2 \n\t" \ +"add r27, r1 \n\t" \ +"adc %A0, r26 \n\t" \ +"adc %B0, r26 \n\t" \ +"lsr r27 \n\t" \ +"adc %A0, r26 \n\t" \ +"adc %B0, r26 \n\t" \ +"clr r1 \n\t" \ +: \ +"=&r" (intRes) \ +: \ +"d" (longIn1), \ +"d" (longIn2) \ +: \ +"r26" , "r27" \ +) + +// Some useful constants + +#define ENABLE_STEPPER_DRIVER_INTERRUPT() TIMSK1 |= (1< +// +// The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates +// first block->accelerate_until step_events_completed, then keeps going at constant speed until +// step_events_completed reaches block->decelerate_after after which it decelerates until the trapezoid generator is reset. +// The slope of acceleration is calculated with the leib ramp alghorithm. + +void st_wake_up() { + // TCNT1 = 0; + if(busy == false) + ENABLE_STEPPER_DRIVER_INTERRUPT(); +} + +FORCE_INLINE unsigned short calc_timer(unsigned short step_rate) { + unsigned short timer; + if(step_rate > MAX_STEP_FREQUENCY) step_rate = MAX_STEP_FREQUENCY; + + if(step_rate > 20000) { // If steprate > 20kHz >> step 4 times + step_rate = (step_rate >> 2)&0x3fff; + step_loops = 4; + } + else if(step_rate > 10000) { // If steprate > 10kHz >> step 2 times + step_rate = (step_rate >> 1)&0x7fff; + step_loops = 2; + } + else { + step_loops = 1; + } + + if(step_rate < 32) step_rate = 32; + step_rate -= 32; // Correct for minimal speed + if(step_rate >= (8*256)){ // higher step rate + unsigned short table_address = (unsigned short)&speed_lookuptable_fast[(unsigned char)(step_rate>>8)][0]; + unsigned char tmp_step_rate = (step_rate & 0x00ff); + unsigned short gain = (unsigned short)pgm_read_word_near(table_address+2); + MultiU16X8toH16(timer, tmp_step_rate, gain); + timer = (unsigned short)pgm_read_word_near(table_address) - timer; + } + else { // lower step rates + unsigned short table_address = (unsigned short)&speed_lookuptable_slow[0][0]; + table_address += ((step_rate)>>1) & 0xfffc; + timer = (unsigned short)pgm_read_word_near(table_address); + timer -= (((unsigned short)pgm_read_word_near(table_address+2) * (unsigned char)(step_rate & 0x0007))>>3); + } + //if(timer < 100) { timer = 100; MSerial.print("Steprate to high : "); MSerial.println(step_rate); }//(20kHz this should never happen) + + //TEMPORARY WORKAROUND FOR ZIT BUG + if ( timer > 2000 ) timer = 2000; + + return timer; +} + + +//DEBUGGING +float zadvance; + +// Initializes the trapezoid generator from the current block. Called whenever a new +// block begins. +FORCE_INLINE void trapezoid_generator_reset() { + #ifdef ADVANCE + advance = current_block->initial_advance; + final_advance = current_block->final_advance; + // Do E steps + advance steps + e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); + old_advance = advance >>8; + zadvance = advance; + #endif + deceleration_time = 0; + // step_rate to timer interval + acc_step_rate = current_block->initial_rate; + acceleration_time = calc_timer(acc_step_rate); + OCR1A = acceleration_time; + OCR1A_nominal = calc_timer(current_block->nominal_rate); + #ifdef Z_LATE_ENABLE + if(current_block->steps_z > 0) stepperInterface[Z_AXIS].setEnabled(true); + #endif +} + +// "The Stepper Driver Interrupt" - This timer interrupt is the workhorse. +// It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. +void st_interrupt() +{ + // If there is no current block, attempt to pop one from the buffer + if (current_block == NULL) { + // Anything in the buffer? + current_block = plan_get_current_block(); + if (current_block != NULL) { + trapezoid_generator_reset(); + counter_x = -(current_block->step_event_count >> 1); + counter_y = counter_x; + counter_z = counter_x; + counter_e = counter_x; + step_events_completed = 0; +// #ifdef ADVANCE +// e_steps[current_block->active_extruder] = 0; +// #endif + } + else { + OCR1A=2000; // 1kHz. + } + } + + if (current_block != NULL) { + // Set directions TO DO This should be done once during init of trapezoid. Endstops -> interrupt + out_bits = current_block->direction_bits; + + // Set direction en check limit switches + if ((out_bits & (1<steps_e; + if (counter_e > 0) { + counter_e -= current_block->step_event_count; + if ((out_bits & (1<active_extruder]--; + } + else { + e_steps[current_block->active_extruder]++; + } + } + #endif //ADVANCE + + counter_x += current_block->steps_x; + if (counter_x > 0) { + stepperInterface[X_AXIS].step(true); + counter_x -= current_block->step_event_count; + stepperInterface[X_AXIS].step(false); + count_position[X_AXIS]+=count_direction[X_AXIS]; + } + + counter_y += current_block->steps_y; + if (counter_y > 0) { + stepperInterface[Y_AXIS].step(true); + counter_y -= current_block->step_event_count; + stepperInterface[Y_AXIS].step(false); + count_position[Y_AXIS]+=count_direction[Y_AXIS]; + } + + counter_z += current_block->steps_z; + if (counter_z > 0) { + stepperInterface[Z_AXIS].step(true); + counter_z -= current_block->step_event_count; + stepperInterface[Z_AXIS].step(false); + count_position[Z_AXIS]+=count_direction[Z_AXIS]; + } + + #ifndef ADVANCE + counter_e += current_block->steps_e; + if (counter_e > 0) { + stepperInterface[E_AXIS].step(true); + counter_e -= current_block->step_event_count; + stepperInterface[E_AXIS].step(false); + count_position[E_AXIS]+=count_direction[E_AXIS]; + } + #endif //!ADVANCE + step_events_completed += 1; + if(step_events_completed >= current_block->step_event_count) break; + } + // Calculare new timer value + unsigned short timer; + unsigned short step_rate; + if (step_events_completed <= (uint32_t)current_block->accelerate_until) { + + MultiU24X24toH16(acc_step_rate, acceleration_time, current_block->acceleration_rate); + acc_step_rate += current_block->initial_rate; + + // upper limit + if(acc_step_rate > current_block->nominal_rate) + acc_step_rate = current_block->nominal_rate; + + // step_rate to timer interval + timer = calc_timer(acc_step_rate); + OCR1A = timer; + acceleration_time += timer; + #ifdef ADVANCE + for(int8_t i=0; i < step_loops; i++) { + advance += advance_rate; + } + //if(advance > current_block->advance) advance = current_block->advance; + // Do E steps + advance steps + e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); + old_advance = advance >>8; + + #endif + } + else if (step_events_completed > (uint32_t)current_block->decelerate_after) { + MultiU24X24toH16(step_rate, deceleration_time, current_block->acceleration_rate); + + if(step_rate > acc_step_rate) { // Check step_rate stays positive + step_rate = current_block->final_rate; + } + else { + step_rate = acc_step_rate - step_rate; // Decelerate from aceleration end point. + } + + // lower limit + if(step_rate < current_block->final_rate) + step_rate = current_block->final_rate; + + // step_rate to timer interval + timer = calc_timer(step_rate); + OCR1A = timer; + deceleration_time += timer; + #ifdef ADVANCE + for(int8_t i=0; i < step_loops; i++) { + advance -= advance_rate; + } + if(advance < final_advance) advance = final_advance; + // Do E steps + advance steps + e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); + old_advance = advance >>8; + #endif //ADVANCE + } + else { + OCR1A = OCR1A_nominal; + } + + // If current block is finished, reset pointer + if (step_events_completed >= current_block->step_event_count) { + current_block = NULL; + plan_discard_current_block(); + } + } +} + +#ifdef ADVANCE +void st_advance_interrupt() + { + OCR4A = 100 * 16; + + // Set E direction (Depends on E direction + advance) + for(unsigned char i=0; i<4;i++) { + if (e_steps[0] != 0) { + stepperInterface[E_AXIS].step(false); + if (e_steps[0] < 0) { + stepperInterface[E_AXIS].setDirection(false); + e_steps[0]++; + stepperInterface[E_AXIS].step(true); + } + else if (e_steps[0] > 0) { + stepperInterface[E_AXIS].setDirection(true); + e_steps[0]--; + stepperInterface[E_AXIS].step(true); + } + } + #if EXTRUDERS > 1 + if (e_steps[1] != 0) { + //stepperInterface[?].step(false); + if (e_steps[1] < 0) { + stepperInterface[?].setDirection(false); + e_steps[1]++; + //stepperInterface[?].step(true); + } + else if (e_steps[1] > 0) { + stepperInterface[?].setDirection(true); + e_steps[1]--; + //stepperInterface[?].step(true); + } + } + #endif + #if EXTRUDERS > 2 + if (e_steps[2] != 0) { + //stepperInterface[?].step(false); + if (e_steps[2] < 0) { + //stepperInterface[?].setDirection(false); + e_steps[2]++; + //stepperInterface[?].step(true); + } + else if (e_steps[2] > 0) { + //stepperInterface[?].setDirection(true); + e_steps[2]--; + //stepperInterface[?].step(true); + } + } + #endif + } + } +#endif // ADVANCE + +void st_init() +{ + //Grab the stepper interfaces + stepperInterface = Motherboard::getBoard().getStepperAllInterfaces(); + + Motherboard::getBoard().setupAccelStepperTimer(); + + #ifdef ADVANCE + e_steps[0] = 0; + e_steps[1] = 0; + e_steps[2] = 0; + #endif //ADVANCE +} + +// Block until all buffered steps are executed +bool st_empty() +{ + if ( blocks_queued() ) return false; + return true; +} + +void st_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e) +{ + CRITICAL_SECTION_START; + count_position[X_AXIS] = x; + count_position[Y_AXIS] = y; + count_position[Z_AXIS] = z; + count_position[E_AXIS] = e; + CRITICAL_SECTION_END; +} + +void st_set_e_position(const int32_t &e) +{ + CRITICAL_SECTION_START; + count_position[E_AXIS] = e; + CRITICAL_SECTION_END; +} + +int32_t st_get_position(uint8_t axis) +{ + int32_t count_pos; + CRITICAL_SECTION_START; + count_pos = count_position[axis]; + CRITICAL_SECTION_END; + return count_pos; +} + +void quickStop() +{ + DISABLE_STEPPER_DRIVER_INTERRUPT(); + while(blocks_queued()) + plan_discard_current_block(); + ENABLE_STEPPER_DRIVER_INTERRUPT(); +} + +#endif diff --git a/firmware/src/shared/StepperAccel.hh b/firmware/src/shared/StepperAccel.hh new file mode 100644 index 0000000..5f269c0 --- /dev/null +++ b/firmware/src/shared/StepperAccel.hh @@ -0,0 +1,72 @@ +/* + StepperAccel.hh - stepper motor driver: executes motion plans of planner.c using the stepper motors + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + + Grbl 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, or + (at your option) any later version. + + Grbl 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 Grbl. If not, see . +*/ + +#ifndef STEPPERACCEL_HH +#define STEPPERACCEL_HH + +#include +#include "StepperAccelPlanner.hh" + +#define MAX_STEP_FREQUENCY 40000 // Max step frequency for Ultimaker (5000 pps / half step) + +#ifndef CRITICAL_SECTION_START + #define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli(); + #define CRITICAL_SECTION_END SREG = _sreg; +#endif //CRITICAL_SECTION_START + +enum AxisEnum {X_AXIS=0, Y_AXIS=1, Z_AXIS=2, E_AXIS=3}; + + +// of the buffer and all stops. This should not be much greater than zero and should only be changed +// if unwanted behavior is observed on a user's machine when running at very slow speeds. +#define MINIMUM_PLANNER_SPEED 2.0 // (mm/sec) + +const int dropsegments=5; //everything with less than this number of steps will be ignored as move and joined with the next movement + +#define EXTRUDERS 1 + +// Initialize and start the stepper motor subsystem +void st_init(); + +// Returns true is there are no buffered steps to be executed +bool st_empty(); + +// Set current position in steps +void st_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e); +void st_set_e_position(const int32_t &e); + +// Get current position in steps +int32_t st_get_position(uint8_t axis); + +// The stepper subsystem goes to sleep when it runs out of things to execute. Call this +// to notify the subsystem that it is time to go to work. +void st_wake_up(); + +void st_interrupt(); + +void st_advance_interrupt(); + +extern block_t *current_block; // A pointer to the block currently being traced + +void quickStop(); + +//DEBUGGING +extern float zadvance; +#endif diff --git a/firmware/src/shared/StepperAccelPlanner.cc b/firmware/src/shared/StepperAccelPlanner.cc new file mode 100644 index 0000000..b4c1ce5 --- /dev/null +++ b/firmware/src/shared/StepperAccelPlanner.cc @@ -0,0 +1,650 @@ +/* + StepperAccelPlanner.cc - buffers movement commands and manages the acceleration profile plan + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + + Grbl 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, or + (at your option) any later version. + + Grbl 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 Grbl. If not, see . +*/ + +/* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */ + +/* + Reasoning behind the mathematics in this module (in the key of 'Mathematica'): + + s == speed, a == acceleration, t == time, d == distance + + Basic definitions: + + Speed[s_, a_, t_] := s + (a*t) + Travel[s_, a_, t_] := Integrate[Speed[s, a, t], t] + + Distance to reach a specific speed with a constant acceleration: + + Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, d, t] + d -> (m^2 - s^2)/(2 a) --> estimate_acceleration_distance() + + Speed after a given distance of travel with constant acceleration: + + Solve[{Speed[s, a, t] == m, Travel[s, a, t] == d}, m, t] + m -> Sqrt[2 a d + s^2] + + DestinationSpeed[s_, a_, d_] := Sqrt[2 a d + s^2] + + When to start braking (di) to reach a specified destionation speed (s2) after accelerating + from initial speed s1 without ever stopping at a plateau: + + Solve[{DestinationSpeed[s1, a, di] == DestinationSpeed[s2, a, d - di]}, di] + di -> (2 a d - s1^2 + s2^2)/(4 a) --> intersection_distance() + + IntersectionDistance[s1_, s2_, a_, d_] := (2 a d - s1^2 + s2^2)/(4 a) +*/ + +#include "Configuration.hh" + +#ifdef HAS_STEPPER_ACCELERATION + + +#include "StepperAccelPlanner.hh" +#include +#include +#include +#include +#include "StepperAccel.hh" +#include "StepperInterface.hh" +#include "Motherboard.hh" + +#ifdef abs +#undef abs +#endif + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) +#define abs(x) ((x)>0?(x):-(x)) + +#define ZSQUARE(x) ((x)*(x)) + +#define VEPSILON 1.0e-5 + +// v1 != v2 +#define VNEQ(v1,v2) (abs((v1)-(v2)) > VEPSILON) + +//=========================================================================== +//=============================public variables ============================ +//=========================================================================== + +uint32_t minsegmenttime; +float max_feedrate[4]; // set the max speeds +float axis_steps_per_unit[4]; +uint32_t max_acceleration_units_per_sq_second[4]; // Use M201 to override by software +float minimumfeedrate; +float p_acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX +float p_retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX +float max_xy_jerk; //speed than can be stopped at once, if i understand correctly. +float max_xy_jerk_squared; // max_xy_jerk * max_xy_jerk +float max_z_jerk; +float mintravelfeedrate; +uint32_t axis_steps_per_sqr_second[NUM_AXIS]; +float extrution_area, extruder_advance_k, steps_per_cubic_mm_e; + +// The current position of the tool in absolute steps +int32_t position[4]; //rescaled from extern when axis_steps_per_unit are changed by gcode +static float previous_speed[4]; // Speed of previous path line segment +static float previous_nominal_speed; // Nominal speed of previous path line segment + +static StepperInterface *stepperInterface; + +//=========================================================================== +//=================semi-private variables, used in inline functions ===== +//=========================================================================== +block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions +volatile unsigned char block_buffer_head; // Index of the next block to be pushed +volatile unsigned char block_buffer_tail; // Index of the block to process now + + +// Returns the index of the next block in the ring buffer +// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication. +static int8_t next_block_index(int8_t block_index) { + block_index++; + if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; } + return(block_index); +} + + +// Returns the index of the previous block in the ring buffer +static int8_t prev_block_index(int8_t block_index) { + if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; } + block_index--; + return(block_index); +} + +//=========================================================================== +//=============================functions ============================ +//=========================================================================== + +// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the +// given acceleration: +FORCE_INLINE float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration) +{ + if (acceleration!=0) { + return((target_rate*target_rate-initial_rate*initial_rate)/ + (2.0*acceleration)); + } + else { + return 0.0; // acceleration was 0, set acceleration distance to 0 + } +} + +// This function gives you the point at which you must start braking (at the rate of -acceleration) if +// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after +// a total travel of distance. This can be used to compute the intersection point between acceleration and +// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) + +FORCE_INLINE float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance) +{ + if (acceleration!=0) { + return((2.0*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/ + (4.0*acceleration) ); + } + else { + return 0.0; // acceleration was 0, set intersection distance to 0 + } +} + +// Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors. + +void calculate_trapezoid_for_block(block_t *block, float entry_factor, float exit_factor) { + uint32_t initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min) + uint32_t final_rate = ceil(block->nominal_rate*exit_factor); // (step/min) + + // Limit minimal step rate (Otherwise the timer will overflow.) + if(initial_rate <120) {initial_rate=120; } + if(final_rate < 120) {final_rate=120; } + + int32_t acceleration = block->acceleration_st; + int32_t accelerate_steps = + ceil(estimate_acceleration_distance(block->initial_rate, block->nominal_rate, acceleration)); + int32_t decelerate_steps = + floor(estimate_acceleration_distance(block->nominal_rate, block->final_rate, -acceleration)); + + // Calculate the size of Plateau of Nominal Rate. + int32_t plateau_steps = block->step_event_count-accelerate_steps-decelerate_steps; + + // Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will + // have to use intersection_distance() to calculate when to abort acceleration and start braking + // in order to reach the final_rate exactly at the end of this block. + if (plateau_steps < 0) { + accelerate_steps = ceil( + intersection_distance(block->initial_rate, block->final_rate, acceleration, block->step_event_count)); + accelerate_steps = max(accelerate_steps,0); // Check limits due to numerical round-off + accelerate_steps = min(accelerate_steps,block->step_event_count); + plateau_steps = 0; + } + + #ifdef ADVANCE + volatile int32_t initial_advance = block->advance*entry_factor*entry_factor; + volatile int32_t final_advance = block->advance*exit_factor*exit_factor; + #endif // ADVANCE + + // block->accelerate_until = accelerate_steps; + // block->decelerate_after = accelerate_steps+plateau_steps; + CRITICAL_SECTION_START; // Fill variables used by the stepper in a critical section + if(block->busy == false) { // Don't update variables if block is busy. + block->accelerate_until = accelerate_steps; + block->decelerate_after = accelerate_steps+plateau_steps; + block->initial_rate = initial_rate; + block->final_rate = final_rate; + #ifdef ADVANCE + block->initial_advance = initial_advance; + block->final_advance = final_advance; + #endif //ADVANCE + } + CRITICAL_SECTION_END; +} + +// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the +// acceleration within the allotted distance. +FORCE_INLINE float max_allowable_speed(float acceleration, float target_velocity, float distance) { + return sqrt(target_velocity*target_velocity-2*acceleration*distance); +} + +// The kernel called by planner_recalculate() when scanning the plan from last to first entry. +void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *next) { + if(!current) { return; } + + if (next) { + // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising. + // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and + // check for maximum allowable speed reductions to ensure maximum possible planned speed. + if (VNEQ(current->entry_speed, current->max_entry_speed)) { + + // If nominal length true, max junction speed is guaranteed to be reached. Only compute + // for max allowable speed if block is decelerating and nominal length is false. + if ((!current->nominal_length_flag) && (current->max_entry_speed > next->entry_speed)) { + current->entry_speed = min( current->max_entry_speed, + max_allowable_speed(-current->acceleration,next->entry_speed,current->millimeters)); + } else { + current->entry_speed = current->max_entry_speed; + } + current->recalculate_flag = true; + + } + } // Skip last block. Already initialized and set for recalculation. +} + +// planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This +// implements the reverse pass. +void planner_reverse_pass() { + uint8_t block_index = block_buffer_head; + if(((block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1)) > 3) { + block_index = (block_buffer_head - 3) & (BLOCK_BUFFER_SIZE - 1); + block_t *block[3] = { NULL, NULL, NULL }; + while(block_index != block_buffer_tail) { + block_index = prev_block_index(block_index); + block[2]= block[1]; + block[1]= block[0]; + block[0] = &block_buffer[block_index]; + planner_reverse_pass_kernel(block[0], block[1], block[2]); + } + } +} + +// The kernel called by planner_recalculate() when scanning the plan from first to last entry. +void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *next) { + if(!previous) { return; } + + // If the previous block is an acceleration block, but it is not long enough to complete the + // full speed change within the block, we need to adjust the entry speed accordingly. Entry + // speeds have already been reset, maximized, and reverse planned by reverse planner. + // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. + if (!previous->nominal_length_flag) { + if (VNEQ(previous->entry_speed, current->entry_speed)) { + double entry_speed = min( current->entry_speed, + max_allowable_speed(-previous->acceleration,previous->entry_speed,previous->millimeters) ); + + // Check for junction speed change + if (VNEQ(current->entry_speed, entry_speed)) { + current->entry_speed = entry_speed; + current->recalculate_flag = true; + } + } + } +} + +// planner_recalculate() needs to go over the current plan twice. Once in reverse and once forward. This +// implements the forward pass. +void planner_forward_pass() { + uint8_t block_index = block_buffer_tail; + block_t *block[3] = { NULL, NULL, NULL }; + + while(block_index != block_buffer_head) { + block[0] = block[1]; + block[1] = block[2]; + block[2] = &block_buffer[block_index]; + planner_forward_pass_kernel(block[0],block[1],block[2]); + block_index = next_block_index(block_index); + } + planner_forward_pass_kernel(block[1], block[2], NULL); +} + +// Recalculates the trapezoid speed profiles for all blocks in the plan according to the +// entry_factor for each junction. Must be called by planner_recalculate() after +// updating the blocks. +void planner_recalculate_trapezoids() { + int8_t block_index = block_buffer_tail; + block_t *current; + block_t *next = NULL; + + while(block_index != block_buffer_head) { + current = next; + next = &block_buffer[block_index]; + if (current) { + // Recalculate if current block entry or exit junction speed has changed. + if (current->recalculate_flag || next->recalculate_flag) { + // NOTE: Entry and exit factors always > 0 by all previous logic operations. + calculate_trapezoid_for_block(current, current->entry_speed/current->nominal_speed, + next->entry_speed/current->nominal_speed); + current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed + } + } + block_index = next_block_index( block_index ); + } + // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated. + if(next != NULL) { + calculate_trapezoid_for_block(next, next->entry_speed/next->nominal_speed, + MINIMUM_PLANNER_SPEED/next->nominal_speed); + next->recalculate_flag = false; + } +} + +// Recalculates the motion plan according to the following algorithm: +// +// 1. Go over every block in reverse order and calculate a junction speed reduction (i.e. block_t.entry_factor) +// so that: +// a. The junction jerk is within the set limit +// b. No speed reduction within one block requires faster deceleration than the one, true constant +// acceleration. +// 2. Go over every block in chronological order and dial down junction speed reduction values if +// a. The speed increase within one block would require faster accelleration than the one, true +// constant acceleration. +// +// When these stages are complete all blocks have an entry_factor that will allow all speed changes to +// be performed using only the one, true constant acceleration, and where no junction jerk is jerkier than +// the set limit. Finally it will: +// +// 3. Recalculate trapezoids for all blocks. + +void planner_recalculate() { + planner_reverse_pass(); + planner_forward_pass(); + planner_recalculate_trapezoids(); +} + + +void plan_init(float extruderAdvanceK, float filamentDiameter, float axis_steps_per_unit_e) { + stepperInterface = Motherboard::getBoard().getStepperAllInterfaces(); + block_buffer_head = 0; + block_buffer_tail = 0; + memset(position, 0, sizeof(position)); // clear position + previous_speed[0] = 0.0; + previous_speed[1] = 0.0; + previous_speed[2] = 0.0; + previous_speed[3] = 0.0; + previous_nominal_speed = 0.0; + + extruder_advance_k = extruderAdvanceK; + extrution_area = 0.25 * filamentDiameter * filamentDiameter * 3.14159; + steps_per_cubic_mm_e = axis_steps_per_unit_e / extrution_area; +} + + + +float junction_deviation = 0.1; +// Add a new linear movement to the buffer. steps x, y and z is the absolute position in +// steps. Microseconds specify how many microseconds the move should take to perform. To aid acceleration +// calculation the caller must also provide the physical length of the line in millimeters. +void plan_buffer_line(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e, float feed_rate, const uint8_t &extruder) +{ + // Calculate the buffer head after we push this byte + int next_buffer_head = next_block_index(block_buffer_head); + + // The target position of the tool in absolute steps + // Calculate target position in absolute steps + //this should be done after the wait, because otherwise a M92 code within the gcode disrupts this calculation somehow + int32_t target[4]; + target[X_AXIS] = x; + target[Y_AXIS] = y; + target[Z_AXIS] = z; + target[E_AXIS] = e; + + // Prepare to set up new block + block_t *block = &block_buffer[block_buffer_head]; + + // Mark block as not busy (Not executed by the stepper interrupt) + block->busy = false; + + // Number of steps for each axis + block->steps_x = labs(target[X_AXIS]-position[X_AXIS]); + block->steps_y = labs(target[Y_AXIS]-position[Y_AXIS]); + block->steps_z = labs(target[Z_AXIS]-position[Z_AXIS]); + block->steps_e = labs(target[E_AXIS]-position[E_AXIS]); + block->step_event_count = max(block->steps_x, max(block->steps_y, max(block->steps_z, block->steps_e))); + + // Bail if this is a zero-length block + if (block->step_event_count <=dropsegments) { return; }; + + // Compute direction bits for this block + block->direction_bits = 0; + if (target[X_AXIS] < position[X_AXIS]) { block->direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<direction_bits |= (1<active_extruder = extruder; + + //enable active axes + if(block->steps_x != 0) stepperInterface[X_AXIS].setEnabled(true); + if(block->steps_y != 0) stepperInterface[Y_AXIS].setEnabled(true); + if(block->steps_z != 0) stepperInterface[Z_AXIS].setEnabled(true); + + // Enable all + if(block->steps_e != 0) { + stepperInterface[E_AXIS].setEnabled(true); + } + + float delta_mm[4]; + delta_mm[X_AXIS] = (target[X_AXIS]-position[X_AXIS])/axis_steps_per_unit[X_AXIS]; + delta_mm[Y_AXIS] = (target[Y_AXIS]-position[Y_AXIS])/axis_steps_per_unit[Y_AXIS]; + delta_mm[Z_AXIS] = (target[Z_AXIS]-position[Z_AXIS])/axis_steps_per_unit[Z_AXIS]; + delta_mm[E_AXIS] = (target[E_AXIS]-position[E_AXIS])/axis_steps_per_unit[E_AXIS]; + if ( block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0 ) { + block->millimeters = abs(delta_mm[E_AXIS]); + } else { + block->millimeters = sqrt(ZSQUARE(delta_mm[X_AXIS]) + ZSQUARE(delta_mm[Y_AXIS]) + + ZSQUARE(delta_mm[Z_AXIS]) + ZSQUARE(delta_mm[E_AXIS])); + } + float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides + + // Calculate speed in mm/second for each axis. No divide by zero due to previous checks. + float inverse_second = feed_rate * inverse_millimeters; + + block->nominal_speed = block->millimeters * inverse_second; // (mm/sec) Always > 0 + block->nominal_rate = ceil(block->step_event_count * inverse_second); // (step/sec) Always > 0 + + + + + if (block->steps_e == 0) { + if(feed_rate 1) feed_rate = feed_rate*moves_queued / (BLOCK_BUFFER_SIZE * 0.5); +#endif + + // Calculate speed in mm/sec for each axis + float current_speed[4]; + for(int i=0; i < 4; i++) { + current_speed[i] = delta_mm[i] * inverse_second; + } + + // Limit speed per axis + float speed_factor = 1.0; //factor <=1 do decrease speed + for(int i=0; i < 4; i++) { + if(abs(current_speed[i]) > max_feedrate[i]) + speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i])); + } + + // Correct the speed + if( speed_factor < 1.0) { + for(int i=0; i < 4; i++) { + if(abs(current_speed[i]) > max_feedrate[i]) + speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i])); + } + for(unsigned char i=0; i < 4; i++) { + current_speed[i] *= speed_factor; + } + block->nominal_speed *= speed_factor; + block->nominal_rate *= speed_factor; + } + + // Compute and limit the acceleration rate for the trapezoid generator. + float steps_per_mm = block->step_event_count/block->millimeters; + if(block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0) { + block->acceleration_st = ceil(p_retract_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 + } + else { + block->acceleration_st = ceil(p_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 + // Limit acceleration per axis + if(((float)block->acceleration_st * (float)block->steps_x / (float)block->step_event_count) > axis_steps_per_sqr_second[X_AXIS]) + block->acceleration_st = axis_steps_per_sqr_second[X_AXIS]; + if(((float)block->acceleration_st * (float)block->steps_y / (float)block->step_event_count) > axis_steps_per_sqr_second[Y_AXIS]) + block->acceleration_st = axis_steps_per_sqr_second[Y_AXIS]; + if(((float)block->acceleration_st * (float)block->steps_e / (float)block->step_event_count) > axis_steps_per_sqr_second[E_AXIS]) + block->acceleration_st = axis_steps_per_sqr_second[E_AXIS]; + if(((float)block->acceleration_st * (float)block->steps_z / (float)block->step_event_count ) > axis_steps_per_sqr_second[Z_AXIS]) + block->acceleration_st = axis_steps_per_sqr_second[Z_AXIS]; + } + block->acceleration = block->acceleration_st / steps_per_mm; + block->acceleration_rate = (int32_t)((float)block->acceleration_st * 8.388608); + +#if 0 // Use old jerk for now + // Compute path unit vector + double unit_vec[3]; + + unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters; + unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters; + unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inverse_millimeters; + + // Compute maximum allowable entry speed at junction by centripetal acceleration approximation. + // Let a circle be tangent to both previous and current path line segments, where the junction + // deviation is defined as the distance from the junction to the closest edge of the circle, + // colinear with the circle center. The circular segment joining the two paths represents the + // path of centripetal acceleration. Solve for max velocity based on max acceleration about the + // radius of the circle, defined indirectly by junction deviation. This may be also viewed as + // path width or max_jerk in the previous grbl version. This approach does not actually deviate + // from path, but used as a robust way to compute cornering speeds, as it takes into account the + // nonlinearities of both the junction angle and junction velocity. + double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed + + // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. + if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) { + // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) + // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. + double cos_theta = - previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] + - previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] + - previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; + + // Skip and use default max junction speed for 0 degree acute junction. + if (cos_theta < 0.95) { + vmax_junction = min(previous_nominal_speed,block->nominal_speed); + // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. + if (cos_theta > -0.95) { + // Compute maximum junction velocity based on maximum acceleration and junction deviation + double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. + vmax_junction = min(vmax_junction, + sqrt(block->acceleration * junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) ); + } + } + } +#endif + // Start with a safe speed + float vmax_junction = max_xy_jerk/2; + if(abs(current_speed[Z_AXIS]) > max_z_jerk/2) + vmax_junction = max_z_jerk/2; + vmax_junction = min(vmax_junction, block->nominal_speed); + + if ((moves_queued > 1) && (previous_nominal_speed > 0.0)) { + float jerk_squared = ZSQUARE(current_speed[X_AXIS]-previous_speed[X_AXIS]) + ZSQUARE(current_speed[Y_AXIS]-previous_speed[Y_AXIS]); + + if((previous_speed[X_AXIS] != 0.0) || (previous_speed[Y_AXIS] != 0.0)) { + vmax_junction = block->nominal_speed; + } + if (jerk_squared > max_xy_jerk_squared) { + vmax_junction *= sqrt((max_xy_jerk_squared/jerk_squared)); + } + if(abs(current_speed[Z_AXIS] - previous_speed[Z_AXIS]) > max_z_jerk) { + vmax_junction *= (max_z_jerk/abs(current_speed[Z_AXIS] - previous_speed[Z_AXIS])); + } + } + block->max_entry_speed = vmax_junction; + + // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. + double v_allowable = max_allowable_speed(-block->acceleration,MINIMUM_PLANNER_SPEED,block->millimeters); + block->entry_speed = min(vmax_junction, v_allowable); + + // Initialize planner efficiency flags + // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. + // If a block can de/ac-celerate from nominal speed to zero within the length of the block, then + // the current block and next block junction speeds are guaranteed to always be at their maximum + // junction speeds in deceleration and acceleration, respectively. This is due to how the current + // block nominal speed limits both the current and next maximum junction speeds. Hence, in both + // the reverse and forward planners, the corresponding block junction speed will always be at the + // the maximum junction speed and may always be ignored for any speed reduction checks. + if (block->nominal_speed <= v_allowable) { block->nominal_length_flag = true; } + else { block->nominal_length_flag = false; } + block->recalculate_flag = true; // Always calculate trapezoid for new block + + // Update previous path unit_vector and nominal speed + memcpy(previous_speed, current_speed, sizeof(previous_speed)); // previous_speed[] = current_speed[] + previous_nominal_speed = block->nominal_speed; + + #ifdef ADVANCE + // Calculate advance rate + if((block->steps_e == 0) || (block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0) || ( extruder_advance_k == 0.0 )) { + block->advance_rate = 0; + block->advance = 0; + } + else { + int32_t acc_dist = estimate_acceleration_distance(0, block->nominal_rate, block->acceleration_st); + float advance = (steps_per_cubic_mm_e * extruder_advance_k) * + (current_speed[E_AXIS] * current_speed[E_AXIS] * extrution_area * extrution_area)*256; + block->advance = advance; + if(acc_dist == 0) { + block->advance_rate = 0; + } + else { + block->advance_rate = advance / (float)acc_dist; + } + } + #endif // ADVANCE + + + + + calculate_trapezoid_for_block(block, block->entry_speed/block->nominal_speed, + MINIMUM_PLANNER_SPEED/block->nominal_speed); + + // Move buffer head + block_buffer_head = next_buffer_head; + + // Update position + memcpy(position, target, sizeof(target)); // position[] = target[] + + planner_recalculate(); + st_wake_up(); +} + +void plan_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e) +{ + position[X_AXIS] = x; + position[Y_AXIS] = y; + position[Z_AXIS] = z; + position[E_AXIS] = e; + st_set_position(position[X_AXIS], position[Y_AXIS], position[Z_AXIS], position[E_AXIS]); + previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest. + previous_speed[0] = 0.0; + previous_speed[1] = 0.0; + previous_speed[2] = 0.0; + previous_speed[3] = 0.0; +} + +void plan_set_e_position(const int32_t &e) +{ + position[E_AXIS] = (int32_t)e; + st_set_e_position(position[E_AXIS]); +} + +uint8_t movesplanned() +{ + return (block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1); +} + +#endif diff --git a/firmware/src/shared/StepperAccelPlanner.hh b/firmware/src/shared/StepperAccelPlanner.hh new file mode 100644 index 0000000..92978fd --- /dev/null +++ b/firmware/src/shared/StepperAccelPlanner.hh @@ -0,0 +1,144 @@ +/* + StepperAccelPlanner.hh - buffers movement commands and manages the acceleration profile plan + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + + Grbl 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, or + (at your option) any later version. + + Grbl 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 Grbl. If not, see . +*/ + +// This module is to be considered a sub-module of stepper.c. Please don't include +// this file from any other module. + +#ifndef STEPPERACCELPLANNER_HH +#define STEPPERACCELPLANNER_HH + +#include + +// extruder advance constant (s2/mm3) +// +// advance (steps) = STEPS_PER_CUBIC_MM_E * EXTUDER_ADVANCE_K * cubic mm per second ^ 2 +// +// hooke's law says: force = k * distance +// bernoulli's priniciple says: v ^ 2 / 2 + g . h + pressure / density = constant +// so: v ^ 2 is proportional to number of steps we advance the extruder +#define ADVANCE + +#define SLOWDOWN + +#define NUM_AXIS 4 // The axis order in all axis related arrays is X, Y, Z, E + +// The number of linear motions that can be in the plan at any give time. +// THE BLOCK_BUFFER_SIZE NEEDS TO BE A POWER OF 2, i.g. 8,16,32 because shifts and ors are used to do the ringbuffering. +#define BLOCK_BUFFER_SIZE 32 // maximize block buffer + +#define FORCE_INLINE __attribute__((always_inline)) inline + + + +// This struct is used when buffering the setup for each linear movement "nominal" values are as specified in +// the source g-code and may never actually be reached if acceleration management is active. +typedef struct { + // Fields used by the bresenham algorithm for tracing the line + int32_t steps_x, steps_y, steps_z, steps_e; // Step count along each axis + uint32_t step_event_count; // The number of step events required to complete this block + int32_t accelerate_until; // The index of the step event on which to stop acceleration + int32_t decelerate_after; // The index of the step event on which to start decelerating + int32_t acceleration_rate; // The acceleration rate used for acceleration calculation + unsigned char direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) + unsigned char active_extruder; // Selects the active extruder + #ifdef ADVANCE + int32_t advance_rate; + volatile int32_t initial_advance; + volatile int32_t final_advance; + float advance; + #endif + + // Fields used by the motion planner to manage acceleration + // float speed_x, speed_y, speed_z, speed_e; // Nominal mm/minute for each axis + float nominal_speed; // The nominal speed for this block in mm/min + float entry_speed; // Entry speed at previous-current junction in mm/min + float max_entry_speed; // Maximum allowable junction entry speed in mm/min + float millimeters; // The total travel of this block in mm + float acceleration; // acceleration mm/sec^2 + unsigned char recalculate_flag; // Planner flag to recalculate trapezoids on entry junction + unsigned char nominal_length_flag; // Planner flag for nominal speed always reached + + // Settings for the trapezoid generator + uint32_t nominal_rate; // The nominal step rate for this block in step_events/sec + uint32_t initial_rate; // The jerk-adjusted step rate at start of block + uint32_t final_rate; // The minimal rate at exit + uint32_t acceleration_st; // acceleration steps/sec^2 + volatile char busy; +} block_t; + +// Initialize the motion plan subsystem +void plan_init(float extruderAdvanceK, float filamentDiameter, float axis_steps_per_unit_e); + +// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in +// steps. Feed rate specifies the speed of the motion. +void plan_buffer_line(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e, float feed_rate, const uint8_t &extruder); + +// Set position. Used for G92 instructions. +void plan_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e); +void plan_set_e_position(const int32_t &e); + +uint8_t movesplanned(); //return the nr of buffered moves + +extern uint32_t minsegmenttime; +extern float max_feedrate[4]; // set the max speeds +extern float axis_steps_per_unit[4]; +extern uint32_t max_acceleration_units_per_sq_second[4]; // Use M201 to override by software +extern float minimumfeedrate; +extern float p_acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX +extern float p_retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX +extern float max_xy_jerk; //speed than can be stopped at once, if i understand correctly. +extern float max_xy_jerk_squared; // max_xy_jerk * max_xy_jerk +extern float max_z_jerk; +extern float mintravelfeedrate; +extern uint32_t axis_steps_per_sqr_second[NUM_AXIS]; + +extern block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions +extern volatile unsigned char block_buffer_head; // Index of the next block to be pushed +extern volatile unsigned char block_buffer_tail; +// Called when the current block is no longer needed. Discards the block and makes the memory +// availible for new blocks. +FORCE_INLINE void plan_discard_current_block() +{ + if (block_buffer_head != block_buffer_tail) { + block_buffer_tail = (block_buffer_tail + 1) & (BLOCK_BUFFER_SIZE - 1); + } +} + +// Gets the current block. Returns NULL if buffer empty +FORCE_INLINE block_t *plan_get_current_block() +{ + if (block_buffer_head == block_buffer_tail) { + return(NULL); + } + block_t *block = &block_buffer[block_buffer_tail]; + block->busy = true; + return(block); +} + +// Gets the current block. Returns NULL if buffer empty +FORCE_INLINE bool blocks_queued() +{ + if (block_buffer_head == block_buffer_tail) { + return false; + } + else + return true; +} +#endif diff --git a/firmware/src/shared/StepperAccelSpeedTable.hh b/firmware/src/shared/StepperAccelSpeedTable.hh new file mode 100644 index 0000000..ea3ca69 --- /dev/null +++ b/firmware/src/shared/StepperAccelSpeedTable.hh @@ -0,0 +1,77 @@ +#ifndef STEPPERACCELSPEEDTABLE_HH +#define STEPPERACCELSPEEDTABLE_HH + +#include +#include + +const uint16_t speed_lookuptable_fast[256][2] PROGMEM = {\ +{ 62500, 55556}, { 6944, 3268}, { 3676, 1176}, { 2500, 607}, { 1893, 369}, { 1524, 249}, { 1275, 179}, { 1096, 135}, +{ 961, 105}, { 856, 85}, { 771, 69}, { 702, 58}, { 644, 49}, { 595, 42}, { 553, 37}, { 516, 32}, +{ 484, 28}, { 456, 25}, { 431, 23}, { 408, 20}, { 388, 19}, { 369, 16}, { 353, 16}, { 337, 14}, +{ 323, 13}, { 310, 11}, { 299, 11}, { 288, 11}, { 277, 9}, { 268, 9}, { 259, 8}, { 251, 8}, +{ 243, 8}, { 235, 7}, { 228, 6}, { 222, 6}, { 216, 6}, { 210, 6}, { 204, 5}, { 199, 5}, +{ 194, 5}, { 189, 4}, { 185, 4}, { 181, 4}, { 177, 4}, { 173, 4}, { 169, 4}, { 165, 3}, +{ 162, 3}, { 159, 4}, { 155, 3}, { 152, 3}, { 149, 2}, { 147, 3}, { 144, 3}, { 141, 2}, +{ 139, 3}, { 136, 2}, { 134, 2}, { 132, 3}, { 129, 2}, { 127, 2}, { 125, 2}, { 123, 2}, +{ 121, 2}, { 119, 1}, { 118, 2}, { 116, 2}, { 114, 1}, { 113, 2}, { 111, 2}, { 109, 1}, +{ 108, 2}, { 106, 1}, { 105, 2}, { 103, 1}, { 102, 1}, { 101, 1}, { 100, 2}, { 98, 1}, +{ 97, 1}, { 96, 1}, { 95, 2}, { 93, 1}, { 92, 1}, { 91, 1}, { 90, 1}, { 89, 1}, +{ 88, 1}, { 87, 1}, { 86, 1}, { 85, 1}, { 84, 1}, { 83, 0}, { 83, 1}, { 82, 1}, +{ 81, 1}, { 80, 1}, { 79, 1}, { 78, 0}, { 78, 1}, { 77, 1}, { 76, 1}, { 75, 0}, +{ 75, 1}, { 74, 1}, { 73, 1}, { 72, 0}, { 72, 1}, { 71, 1}, { 70, 0}, { 70, 1}, +{ 69, 0}, { 69, 1}, { 68, 1}, { 67, 0}, { 67, 1}, { 66, 0}, { 66, 1}, { 65, 0}, +{ 65, 1}, { 64, 1}, { 63, 0}, { 63, 1}, { 62, 0}, { 62, 1}, { 61, 0}, { 61, 1}, +{ 60, 0}, { 60, 0}, { 60, 1}, { 59, 0}, { 59, 1}, { 58, 0}, { 58, 1}, { 57, 0}, +{ 57, 1}, { 56, 0}, { 56, 0}, { 56, 1}, { 55, 0}, { 55, 1}, { 54, 0}, { 54, 0}, +{ 54, 1}, { 53, 0}, { 53, 0}, { 53, 1}, { 52, 0}, { 52, 0}, { 52, 1}, { 51, 0}, +{ 51, 0}, { 51, 1}, { 50, 0}, { 50, 0}, { 50, 1}, { 49, 0}, { 49, 0}, { 49, 1}, +{ 48, 0}, { 48, 0}, { 48, 1}, { 47, 0}, { 47, 0}, { 47, 0}, { 47, 1}, { 46, 0}, +{ 46, 0}, { 46, 1}, { 45, 0}, { 45, 0}, { 45, 0}, { 45, 1}, { 44, 0}, { 44, 0}, +{ 44, 0}, { 44, 1}, { 43, 0}, { 43, 0}, { 43, 0}, { 43, 1}, { 42, 0}, { 42, 0}, +{ 42, 0}, { 42, 1}, { 41, 0}, { 41, 0}, { 41, 0}, { 41, 0}, { 41, 1}, { 40, 0}, +{ 40, 0}, { 40, 0}, { 40, 0}, { 40, 1}, { 39, 0}, { 39, 0}, { 39, 0}, { 39, 0}, +{ 39, 1}, { 38, 0}, { 38, 0}, { 38, 0}, { 38, 0}, { 38, 1}, { 37, 0}, { 37, 0}, +{ 37, 0}, { 37, 0}, { 37, 0}, { 37, 1}, { 36, 0}, { 36, 0}, { 36, 0}, { 36, 0}, +{ 36, 1}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 0}, { 35, 1}, +{ 34, 0}, { 34, 0}, { 34, 0}, { 34, 0}, { 34, 0}, { 34, 1}, { 33, 0}, { 33, 0}, +{ 33, 0}, { 33, 0}, { 33, 0}, { 33, 0}, { 33, 1}, { 32, 0}, { 32, 0}, { 32, 0}, +{ 32, 0}, { 32, 0}, { 32, 0}, { 32, 0}, { 32, 1}, { 31, 0}, { 31, 0}, { 31, 0}, +{ 31, 0}, { 31, 0}, { 31, 0}, { 31, 1}, { 30, 0}, { 30, 0}, { 30, 0}, { 30, 0} +}; + +const uint16_t speed_lookuptable_slow[256][2] PROGMEM = {\ +{ 62500, 12500}, { 50000, 8334}, { 41666, 5952}, { 35714, 4464}, { 31250, 3473}, { 27777, 2777}, { 25000, 2273}, { 22727, 1894}, +{ 20833, 1603}, { 19230, 1373}, { 17857, 1191}, { 16666, 1041}, { 15625, 920}, { 14705, 817}, { 13888, 731}, { 13157, 657}, +{ 12500, 596}, { 11904, 541}, { 11363, 494}, { 10869, 453}, { 10416, 416}, { 10000, 385}, { 9615, 356}, { 9259, 331}, +{ 8928, 308}, { 8620, 287}, { 8333, 269}, { 8064, 252}, { 7812, 237}, { 7575, 223}, { 7352, 210}, { 7142, 198}, +{ 6944, 188}, { 6756, 178}, { 6578, 168}, { 6410, 160}, { 6250, 153}, { 6097, 145}, { 5952, 139}, { 5813, 132}, +{ 5681, 126}, { 5555, 121}, { 5434, 115}, { 5319, 111}, { 5208, 106}, { 5102, 102}, { 5000, 99}, { 4901, 94}, +{ 4807, 91}, { 4716, 87}, { 4629, 84}, { 4545, 81}, { 4464, 79}, { 4385, 75}, { 4310, 73}, { 4237, 71}, +{ 4166, 68}, { 4098, 66}, { 4032, 64}, { 3968, 62}, { 3906, 60}, { 3846, 59}, { 3787, 56}, { 3731, 55}, +{ 3676, 53}, { 3623, 52}, { 3571, 50}, { 3521, 49}, { 3472, 48}, { 3424, 46}, { 3378, 45}, { 3333, 44}, +{ 3289, 43}, { 3246, 41}, { 3205, 41}, { 3164, 39}, { 3125, 39}, { 3086, 38}, { 3048, 36}, { 3012, 36}, +{ 2976, 35}, { 2941, 35}, { 2906, 33}, { 2873, 33}, { 2840, 32}, { 2808, 31}, { 2777, 30}, { 2747, 30}, +{ 2717, 29}, { 2688, 29}, { 2659, 28}, { 2631, 27}, { 2604, 27}, { 2577, 26}, { 2551, 26}, { 2525, 25}, +{ 2500, 25}, { 2475, 25}, { 2450, 23}, { 2427, 24}, { 2403, 23}, { 2380, 22}, { 2358, 22}, { 2336, 22}, +{ 2314, 21}, { 2293, 21}, { 2272, 20}, { 2252, 20}, { 2232, 20}, { 2212, 20}, { 2192, 19}, { 2173, 18}, +{ 2155, 19}, { 2136, 18}, { 2118, 18}, { 2100, 17}, { 2083, 17}, { 2066, 17}, { 2049, 17}, { 2032, 16}, +{ 2016, 16}, { 2000, 16}, { 1984, 16}, { 1968, 15}, { 1953, 16}, { 1937, 14}, { 1923, 15}, { 1908, 15}, +{ 1893, 14}, { 1879, 14}, { 1865, 14}, { 1851, 13}, { 1838, 14}, { 1824, 13}, { 1811, 13}, { 1798, 13}, +{ 1785, 12}, { 1773, 13}, { 1760, 12}, { 1748, 12}, { 1736, 12}, { 1724, 12}, { 1712, 12}, { 1700, 11}, +{ 1689, 12}, { 1677, 11}, { 1666, 11}, { 1655, 11}, { 1644, 11}, { 1633, 10}, { 1623, 11}, { 1612, 10}, +{ 1602, 10}, { 1592, 10}, { 1582, 10}, { 1572, 10}, { 1562, 10}, { 1552, 9}, { 1543, 10}, { 1533, 9}, +{ 1524, 9}, { 1515, 9}, { 1506, 9}, { 1497, 9}, { 1488, 9}, { 1479, 9}, { 1470, 9}, { 1461, 8}, +{ 1453, 8}, { 1445, 9}, { 1436, 8}, { 1428, 8}, { 1420, 8}, { 1412, 8}, { 1404, 8}, { 1396, 8}, +{ 1388, 7}, { 1381, 8}, { 1373, 7}, { 1366, 8}, { 1358, 7}, { 1351, 7}, { 1344, 8}, { 1336, 7}, +{ 1329, 7}, { 1322, 7}, { 1315, 7}, { 1308, 6}, { 1302, 7}, { 1295, 7}, { 1288, 6}, { 1282, 7}, +{ 1275, 6}, { 1269, 7}, { 1262, 6}, { 1256, 6}, { 1250, 7}, { 1243, 6}, { 1237, 6}, { 1231, 6}, +{ 1225, 6}, { 1219, 6}, { 1213, 6}, { 1207, 6}, { 1201, 5}, { 1196, 6}, { 1190, 6}, { 1184, 5}, +{ 1179, 6}, { 1173, 5}, { 1168, 6}, { 1162, 5}, { 1157, 5}, { 1152, 6}, { 1146, 5}, { 1141, 5}, +{ 1136, 5}, { 1131, 5}, { 1126, 5}, { 1121, 5}, { 1116, 5}, { 1111, 5}, { 1106, 5}, { 1101, 5}, +{ 1096, 5}, { 1091, 5}, { 1086, 4}, { 1082, 5}, { 1077, 5}, { 1072, 4}, { 1068, 5}, { 1063, 4}, +{ 1059, 5}, { 1054, 4}, { 1050, 4}, { 1046, 5}, { 1041, 4}, { 1037, 4}, { 1033, 5}, { 1028, 4}, +{ 1024, 4}, { 1020, 4}, { 1016, 4}, { 1012, 4}, { 1008, 4}, { 1004, 4}, { 1000, 4}, { 996, 4}, +{ 992, 4}, { 988, 4}, { 984, 4}, { 980, 4}, { 976, 4}, { 972, 4}, { 968, 3}, { 965, 3} +}; + +#endif diff --git a/firmware/src/shared/StepperAxis.cc b/firmware/src/shared/StepperAxis.cc index 9bb3aa2..115b3fd 100644 --- a/firmware/src/shared/StepperAxis.cc +++ b/firmware/src/shared/StepperAxis.cc @@ -17,6 +17,7 @@ void StepperAxis::setTarget(const int32_t target_in, } else { delta = target - position; } + absoluteTarget = position + delta; direction = true; if (delta != 0) { interface->setEnabled(true); @@ -50,6 +51,7 @@ void StepperAxis::reset() { minimum = 0; maximum = 0; target = 0; + absoluteTarget = 0; counter = 0; delta = 0; #if defined(SINGLE_SWITCH_ENDSTOPS) && (SINGLE_SWITCH_ENDSTOPS == 1) @@ -152,3 +154,18 @@ bool StepperAxis::isAtMaximum() { bool StepperAxis::isAtMinimum() { return interface->isAtMinimum(); } + + +void StepperAxis::step() { + interface->setDirection(direction); + bool hit_endstop = checkEndstop(false); + if (!hit_endstop) interface->step(true); + if (direction) position++; + else position--; + interface->step(false); +} + + +void StepperAxis::setDirection(bool dir) { + direction = dir; +} diff --git a/firmware/src/shared/StepperAxis.hh b/firmware/src/shared/StepperAxis.hh index a74999b..4484106 100644 --- a/firmware/src/shared/StepperAxis.hh +++ b/firmware/src/shared/StepperAxis.hh @@ -15,7 +15,8 @@ public: volatile int32_t position; ///< Current position of this axis, in steps int32_t minimum; ///< Minimum position, in steps int32_t maximum; ///< Maximum position, in steps - volatile int32_t target; ///< Target position, in steps + volatile int32_t target; ///< Target position, in steps (relative or absolute position, depending on how called) + volatile int32_t absoluteTarget;///< Absolute Target position, in steps (absolute position) volatile int32_t counter; ///< Step counter; represents the proportion of ///< a step so far passed. When the counter hits ///< zero, a step is taken. @@ -94,6 +95,13 @@ public: /// \return True if the axis has triggered its minimum endstop bool isAtMinimum(); + //Methods for accelerated stepping + + //Move one step + void step(); + + //Set the direction for the steps + void setDirection(bool dir); }; #endif // STEPPERAXIS_HH From e0cb0c822ece6681ab03f6997274d583e4e3199f Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 14 Sep 2012 19:22:39 -0600 Subject: [PATCH 50/61] Merging G3Firmware34 into G3Firmware repository. Updating for recent s3g compliance testing. --- .gitignore | 2 + dist/bin2hex.py | 130 ++ dist/tools-mac/libusb.la | 35 - dist/tools-mac/libusbpp.la | 35 - docs/jetty_s3g_handling.markdown | 129 ++ firmware/SConstruct | 2 +- firmware/simulator/Configuration.hh | 13 + firmware/simulator/Makefile | 171 ++ firmware/simulator/Simulator.hh | 27 + firmware/simulator/SimulatorRecord.hh | 50 + .../simulator/StepperAccelPlannerExtras.cc | 1190 ++++++++++++ .../simulator/StepperAccelPlannerExtras.hh | 36 + firmware/simulator/planner.cc | 172 ++ firmware/simulator/s3g.c | 435 +++++ firmware/simulator/s3g.h | 312 +++ firmware/simulator/s3g_private.h | 38 + firmware/simulator/s3g_stdio.c | 279 +++ firmware/simulator/s3g_stdio.h | 22 + firmware/simulator/s3gdump.c | 253 +++ firmware/src/Extruder/EepromDefaults.hh | 1 + firmware/src/Extruder/Host.cc | 1 + firmware/src/Motherboard/Command.cc | 246 ++- firmware/src/Motherboard/Command.hh | 14 + firmware/src/Motherboard/EepromMap.cc | 191 +- firmware/src/Motherboard/EepromMap.hh | 199 +- firmware/src/Motherboard/Errors.hh | 5 + firmware/src/Motherboard/Host.cc | 48 +- firmware/src/Motherboard/Host.hh | 4 + firmware/src/Motherboard/Main.cc | 22 +- firmware/src/Motherboard/Point.hh | 2 +- firmware/src/Motherboard/SDCard.cc | 8 + firmware/src/Motherboard/SDCard.hh | 4 + firmware/src/Motherboard/SqrtTable.c | 77 + firmware/src/Motherboard/SqrtTable.hh | 418 ++++ firmware/src/Motherboard/Steppers.cc | 841 ++++++++- firmware/src/Motherboard/Steppers.hh | 36 +- .../Motherboard/boards/mb24/Configuration.hh | 17 + .../Motherboard/boards/mb24/EepromDefaults.hh | 110 ++ .../Motherboard/boards/mb24/Motherboard.cc | 97 +- .../Motherboard/boards/mb24/Motherboard.hh | 5 +- .../boards/rrmbv12-2560/Configuration.hh | 173 ++ .../boards/rrmbv12-2560/EepromDefaults.hh | 75 + .../boards/rrmbv12-2560/Motherboard.cc | 285 +++ .../boards/rrmbv12-2560/Motherboard.hh | 98 + .../boards/rrmbv12/Configuration.hh | 11 +- .../boards/rrmbv12/EepromDefaults.hh | 78 + .../Motherboard/boards/rrmbv12/Motherboard.cc | 102 +- .../Motherboard/boards/rrmbv12/Motherboard.hh | 20 +- firmware/src/Motherboard/eeprom.py | 390 ++++ firmware/src/SConscript.motherboard | 49 +- .../repg_machines/3G-5D-(RPM)-accelerated.xml | 243 +++ .../src/repg_machines/3G-5D-accelerated.xml | 243 +++ .../repg_machines/thingomatic-accelerated.xml | 129 ++ firmware/src/shared/Commands.hh | 12 + firmware/src/shared/Eeprom.cc | 103 +- firmware/src/shared/Eeprom.hh | 23 +- firmware/src/shared/InterfaceBoard.cc | 2 +- firmware/src/shared/InterfaceBoard.hh | 7 - firmware/src/shared/LiquidCrystal.cc | 104 +- firmware/src/shared/LiquidCrystal.hh | 11 +- firmware/src/shared/Marlin-issues.txt | 114 ++ firmware/src/shared/Menu.cc | 1676 ++++++++++------- firmware/src/shared/Menu.hh | 286 +-- firmware/src/shared/MoodLightController.cc | 11 +- firmware/src/shared/StepperAccel.cc | 626 ++++-- firmware/src/shared/StepperAccel.hh | 70 +- firmware/src/shared/StepperAccelPlanner.cc | 1670 +++++++++++++--- firmware/src/shared/StepperAccelPlanner.hh | 214 ++- .../src/shared/StepperAccelSpeedTableBuild.c | 49 + firmware/src/shared/StepperAxis.cc | 4 +- firmware/src/shared/StepperInterface.cc | 5 +- firmware/src/shared/UART.cc | 2 +- firmware/src/shared/avrfix/avrfix.c | 989 ++++++++++ firmware/src/shared/avrfix/avrfix.h | 330 ++++ firmware/src/shared/avrfix/avrfix_config.h | 42 + firmware/src/shared/avrfix/lgpl.txt | 504 +++++ firmware/src/shared/avrfix/readme.txt | 90 + firmware/src/skeinforge_plugins/altshell.py | 146 ++ .../src/skeinforge_plugins/extrusion-35.py | 64 + .../src/skeinforge_plugins/extrusion-40.py | 53 + .../src/skeinforge_plugins/extrusion-44.py | 53 + .../src/skeinforge_plugins/extrusion-47.py | 53 + .../src/skeinforge_plugins/extrusion-50.py | 53 + firmware/src/skeinforge_plugins/install.sh | 37 + tests/g3_tests | 1 + tests/s3g_tests/G3FirmwareTests.py | 927 +++++++++ tests/s3g_tests/G3Firmware_constants.py | 63 + .../G3Firmware_unsupported_functions.py | 50 + tests/s3g_tests/MightyBoard_constants.py | 55 + .../MightyBoard_unsupported_functions.py | 39 + tests/s3g_tests/README.markdown | 217 +++ tests/s3g_tests/s3g | 1 + tests/s3g_tests/test_s3g_functions.py | 174 ++ tests/s3g_tests/test_utilities.py | 116 ++ 94 files changed, 14900 insertions(+), 1719 deletions(-) create mode 100755 dist/bin2hex.py delete mode 100755 dist/tools-mac/libusb.la delete mode 100755 dist/tools-mac/libusbpp.la create mode 100644 docs/jetty_s3g_handling.markdown create mode 100644 firmware/simulator/Configuration.hh create mode 100644 firmware/simulator/Makefile create mode 100644 firmware/simulator/Simulator.hh create mode 100644 firmware/simulator/SimulatorRecord.hh create mode 100644 firmware/simulator/StepperAccelPlannerExtras.cc create mode 100644 firmware/simulator/StepperAccelPlannerExtras.hh create mode 100644 firmware/simulator/planner.cc create mode 100644 firmware/simulator/s3g.c create mode 100644 firmware/simulator/s3g.h create mode 100644 firmware/simulator/s3g_private.h create mode 100644 firmware/simulator/s3g_stdio.c create mode 100644 firmware/simulator/s3g_stdio.h create mode 100644 firmware/simulator/s3gdump.c create mode 100644 firmware/src/Extruder/EepromDefaults.hh create mode 100644 firmware/src/Motherboard/SqrtTable.c create mode 100644 firmware/src/Motherboard/SqrtTable.hh create mode 100644 firmware/src/Motherboard/boards/mb24/EepromDefaults.hh create mode 100644 firmware/src/Motherboard/boards/rrmbv12-2560/Configuration.hh create mode 100644 firmware/src/Motherboard/boards/rrmbv12-2560/EepromDefaults.hh create mode 100644 firmware/src/Motherboard/boards/rrmbv12-2560/Motherboard.cc create mode 100644 firmware/src/Motherboard/boards/rrmbv12-2560/Motherboard.hh create mode 100644 firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh create mode 100644 firmware/src/Motherboard/eeprom.py create mode 100644 firmware/src/repg_machines/3G-5D-(RPM)-accelerated.xml create mode 100644 firmware/src/repg_machines/3G-5D-accelerated.xml create mode 100644 firmware/src/repg_machines/thingomatic-accelerated.xml create mode 100644 firmware/src/shared/Marlin-issues.txt create mode 100644 firmware/src/shared/StepperAccelSpeedTableBuild.c create mode 100644 firmware/src/shared/avrfix/avrfix.c create mode 100644 firmware/src/shared/avrfix/avrfix.h create mode 100644 firmware/src/shared/avrfix/avrfix_config.h create mode 100644 firmware/src/shared/avrfix/lgpl.txt create mode 100644 firmware/src/shared/avrfix/readme.txt create mode 100644 firmware/src/skeinforge_plugins/altshell.py create mode 100644 firmware/src/skeinforge_plugins/extrusion-35.py create mode 100644 firmware/src/skeinforge_plugins/extrusion-40.py create mode 100644 firmware/src/skeinforge_plugins/extrusion-44.py create mode 100644 firmware/src/skeinforge_plugins/extrusion-47.py create mode 100644 firmware/src/skeinforge_plugins/extrusion-50.py create mode 100755 firmware/src/skeinforge_plugins/install.sh create mode 120000 tests/g3_tests create mode 100644 tests/s3g_tests/G3FirmwareTests.py create mode 100644 tests/s3g_tests/G3Firmware_constants.py create mode 100644 tests/s3g_tests/G3Firmware_unsupported_functions.py create mode 100644 tests/s3g_tests/MightyBoard_constants.py create mode 100644 tests/s3g_tests/MightyBoard_unsupported_functions.py create mode 100644 tests/s3g_tests/README.markdown create mode 120000 tests/s3g_tests/s3g create mode 100644 tests/s3g_tests/test_s3g_functions.py create mode 100644 tests/s3g_tests/test_utilities.py diff --git a/.gitignore b/.gitignore index 0143c08..c07b0b2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ build html *.o +*.svn +*.DS_store diff --git a/dist/bin2hex.py b/dist/bin2hex.py new file mode 100755 index 0000000..8076e8e --- /dev/null +++ b/dist/bin2hex.py @@ -0,0 +1,130 @@ +#!/usr/bin/python + +''' +Quick and dirty Python script to convert a eeprom_dump.bin file +to Intel hex format. It assumes that the starting offset in EEPROM +is 0 but you can alter that with a command line switch. + +Dan Newman ( dan . newman @ mtbaldy . us ) +27 July 2012 +''' + +import struct +import sys +import getopt + +DEF_INFILE = 'eeprom_dump.bin' +DEF_OUTFILE = 'eeprom_dump.hex' +DEF_ADDRESS = int( 0 ) + +def usage( prog, exit_stat=0 ): + str = \ +'Usage: %s [-h] [-s starting-address] [-o outfile] [infile]\n' % prog + str += \ +' The input file, infile, defaults to %s\n' % DEF_INFILE + str += \ +' -h, --help\n' + \ +' This usage information\n' + \ +' -o outfile, --output=outfile\n' + \ +' Specify the output file name. The name defaults to %s\n' % DEF_OUTFILE + str += \ +' -s address, --start=address\n' + \ +' The starting address (offset) for the data. The default is %d\n' % DEF_ADDRESS + if exit_stat: + sys.stderr.write( str ) + else: + sys.stdout.write( str ) + sys.exit( exit_stat ) + +# Process the command line switches + +infile = DEF_INFILE +outfile = DEF_OUTFILE +address = DEF_ADDRESS + +try: + opts, args = getopt.getopt( sys.argv[1:], 'ho:s:', + [ 'help', 'output=', 'start=' ] ) + +except: + usage( sys.argv[0], 1 ) + +for opt, val in opts: + if opt in ( '-h', '--help' ): + usage( sys.argv[0], 0 ) + elif opt in ( '-o', '--output' ): + outfile = val + elif opt in ( '-s', '--start' ): + address = int( val, 0 ) + +# Process the command line arguments: there can only be zero or one + +if len( args ) == 1: + infile = args[0] +elif len( args ) > 1: + usage( sys.argv[0], 1 ) + +fin = open( infile, 'rb' ) +if fin < 0: + sys.stderr.write( 'Unable to open the file "%s"\n' % infile ) + sys.exit( 1 ) + +# Read the entire input file + +bytes = fin.read() +fin.close() + +# Create the output file + +fout = open( outfile, 'w' ) +if fout < 0: + sys.stderr.write( 'Unable to create the file "%s"\n' % outfile ) + sys.exit( 1 ) + +# Now loop over the input data, generating an Intel hex file +# We'll process the input data in blocks of 32 bytes + +# Index into the list of bytes read from the input file +index = 0 + +# While loop counter +length = len( bytes ) + +while length > 0: + + # Convert the starting address to big endian + l = struct.pack( '>H', address ) + + # Number of bytes for this line of output + nbytes = 32 + if nbytes > length: + nbytes = length + + # Generate the initial part of the output file's line + # ':' -- start code + # %0.2x -- data length + # %0.2x%0.2x -- starting address as a big-endian, unsigned short + # '00' -- record type + + line = ':%02x%02x%02x00' % ( nbytes, ord( l[0] ), ord( l[1] ) ) + + sum = nbytes + ord( l[0] ) + ord( l[1] ) + + for i in range( 0, nbytes ): + sum += ord( bytes[index + i] ) + line += '%02x' % ord( bytes[index + i] ) + + ''' + Checksum is the 2's complement of the least significant byte of the + sum of the fields. Need to do a final & with 0xff to suppress overflow + ''' + cksum = ( 0x100 - ( 0xff & sum ) ) & 0xff + line += '%02x\n' % cksum + fout.write( line.upper() ) + + index += nbytes + address += nbytes + length -= nbytes + +fout.write( ':00000001FF\n' ) +fout.close() diff --git a/dist/tools-mac/libusb.la b/dist/tools-mac/libusb.la deleted file mode 100755 index 42e3bbc..0000000 --- a/dist/tools-mac/libusb.la +++ /dev/null @@ -1,35 +0,0 @@ -# libusb.la - a libtool library file -# Generated by ltmain.sh - GNU libtool 1.5.6 (1.1220.2.95 2004/04/11 05:50:42) -# -# Please DO NOT delete this file! -# It is necessary for linking the library. - -# The name that we can dlopen(3). -dlname='' - -# Names of this library. -library_names='' - -# The name of the static archive. -old_library='libusb.a' - -# Libraries that this one depends upon. -dependency_libs='' - -# Version information for libusb. -current=8 -age=4 -revision=4 - -# Is this an already installed library? -installed=yes - -# Should we warn about portability when linking against -modules? -shouldnotlink=no - -# Files to dlopen/dlpreopen -dlopen='' -dlpreopen='' - -# Directory that this library needs to be installed in: -libdir='/usr/local/AVRMacPack-20081213/lib' diff --git a/dist/tools-mac/libusbpp.la b/dist/tools-mac/libusbpp.la deleted file mode 100755 index cba48ba..0000000 --- a/dist/tools-mac/libusbpp.la +++ /dev/null @@ -1,35 +0,0 @@ -# libusbpp.la - a libtool library file -# Generated by ltmain.sh - GNU libtool 1.5.6 (1.1220.2.95 2004/04/11 05:50:42) -# -# Please DO NOT delete this file! -# It is necessary for linking the library. - -# The name that we can dlopen(3). -dlname='' - -# Names of this library. -library_names='' - -# The name of the static archive. -old_library='libusbpp.a' - -# Libraries that this one depends upon. -dependency_libs=' /usr/local/AVRMacPack-20081213/lib/libusb.la' - -# Version information for libusbpp. -current=8 -age=4 -revision=4 - -# Is this an already installed library? -installed=yes - -# Should we warn about portability when linking against -modules? -shouldnotlink=no - -# Files to dlopen/dlpreopen -dlopen='' -dlpreopen='' - -# Directory that this library needs to be installed in: -libdir='/usr/local/AVRMacPack-20081213/lib' diff --git a/docs/jetty_s3g_handling.markdown b/docs/jetty_s3g_handling.markdown new file mode 100644 index 0000000..bc8d201 --- /dev/null +++ b/docs/jetty_s3g_handling.markdown @@ -0,0 +1,129 @@ +# Jetty and G3Firmware Firmware Handling of s3g Commands + +## Overview + +The Jetty Firmware is an enhancement to the G3Firmware used by Thing-o-Matics and Cupcakes with the 5D3G shield. As a result, the Jetty Firmware and the G3Firmware implements the same s3g commands. + +In an effort to be concise, this document only lists the s3g commands that have behaviors different from those described in the general s3g specification @ https://github.com/makerbot/s3g/blob/master/doc/s3g_protocol.markdown + +Note that when using the accelerated planner in the Jetty Firmware, only a single extruder is presently supported. Dual extrusion on a Cupcake or Thing-o-Matic is only supported when using the non-accelerated planner. + +## Ignored Commands + +These commands are not implemented by the Host. However, as they are all implemented as "buffered" Host Action commands, the Host acknowledges them with a "Success" response but then ignores the command. + +With the exception of the build notification commands, these s3g commands all post-date the G3Firmware and as such are not known to that firmware. In the case of the build notification commands, they were experimental and not supported by the G3Firmware. Many of the remainder of these commands are not applicable to the G3 or G4 hardware families (e.g., queue song, set digital potentiometer value, etc.). + +### Host Query Commands + +*01 - Initialize firmware to boot state: this command is really a Host Action command, but the s3g protocol specification lists it as a Host Query command?) + +### Host Action Commands + +*145 - Set digital potentiometer value +*146 - Set RGB LED value +*147 - Set beep +*148 - Wait for button +*149 - Display message to LCD +*150 - Set build percentage +*151 - Queue song +*153 - Build start notification +*154 - Build end notification + +### Tool Query Commands + +### Tool Action Commands + +*24 - Toolhead abort immediately: since this command is delivered to the host as the payload in a buffered Host Action command, it is immediately acknowledged with a "Success" response and then ignored. + +## Unsupported Commands (returns "cmd unsupported") + +These s3g commands all post-date the G3Firmware and as such are not known to the G3Firmware. Their issuance to the G3Firmware (or Jetty Firmware) will be met with a "command unsupported" response. + +### Host Query Commands + +*23 - Get motherboard status +*24 - Get build statistics +*27 - Get advanced version + +### Host Action Commands + +### Tool Query Commands + +### Tool Action Commands + +## Commands With Limited or Specialized Behavior + +### Host Query Commands + +#### 00 - Get version: Query firmware for version information + HostVersion <= 26 : returns firmware version 0x0000 (i.e., invalid version) + HostVersion > 26 : returns motherboard firmware version + +#### 03 - Clear buffer: Empty the command buffer + Like the Replicator, the buffer is cleared by performing a soft reset. + +#### 07 - Abort immediately: Stop machine, shut down job permanently + Like the Replicator, this action is performed by executing a soft reset. + +#### 10 - Tool query: Query a tool for information + In the G3Firmware family, the "tool" is a separate device -- a "slave" device -- distinct from the Host. Tool Query commands are relayed to the slave device, and the slave device's response is then relayed back by the host as the response to the query command. Consequently, responses to Tool Query commands have more latency than with the MightyBoard. + +#### 12 / 13 - Read from / Write to EEPROM + Like the MightyBoard firmware, the AVR EEPROM library for EEPROM access is used. That library does not handle invalid values for offset or lengths. As a result, the firmware is most obliging in its attempts to process any supplied value. + +#### 18 - Get next filename + Owing to differences in SD card libraries, the Jetty and G3Firmware do not report the volume name as the first retrieved file name. + +#### 22 - Extended stop: Stop a subset of systems + A zero response is always sent. There is no processed failure case. + +#### 25 - Get communications stats + Always returns a value of zero for the Packets Received field. + +### Host Buffered Commands + +#### 131 - Find axes minimums: Move specified axes in the negative direction until their limit switch is triggered. + The Cupcake does not have endstops. The Thing-o-Matic has X and Y minimum endstops. Thus get axes minimums for the Z axis will result in the stepper trying to move past the minimum point. For the A and B axes, the command will timeout. + +#### 132 - Find axes maximums: Move specified axes in the positive direction until their limit switch is triggered. + The Cupcake does not have endstops. The Thing-o-Matic has only a Z axis maximum endstop. Any attempt to find the axes maximums for the X or Y axes will result in those axes moving past their maximum positions. For the A and B axes, the command will timeout. + +#### 135 - Wait for tool ready : Wait until a tool is ready before proceeding + Either Tool ID 0 or 1 may be selected. + Nominal timeout values are not implemented, all values accepted for timeout. + The delay parameter is ignored + A tool timeout of zero will not wait for the tool to heat. + +#### 136 - Tool action command: Send an action command to a tool for execution + In the G3Firmware family, the "tool" is a separate device -- a "slave" device -- distinct from the Host. Tool Action commands are relayed to the slave device and an immediate "Success" response is returned as Tool Action commands are the payload of a buffered Host Action command. + +#### 141 - Wait for platform ready: Wait until a build platform is ready before proceeding + The bot has no knowledge of nominal values. + Tool ID is ignored. + Delay is ignored + Timeout accepts all values. A timeout of 0 or small will not wait for the tool to heat + +#### 152 - Reset to Factory + With the exception of the home offsets and filament counters, all EEPROM parameters are reset to their default values. Note that they are not written to 0xff but explicitly set to the default value of the parameter stored at that location. + +### Tool Query Commands + +#### 36 - Get tool status + As mentioned in the s3g specification, there is a deprecated form of the response to this command. The G3Firmware and Jetty Firmware generate that legacy form. As such, statuses temperature dropping, not heating, software cutoff, and not plugged are not generated. Instead the AVR WDRF, BORF, EXTRF, and PORF bit flag statuses are returned. + Also, the G3Firmware and Jetty Firmware do detect reliably platform error caused by an open connection. The v3.6 Extruder Controller with the v3.1 Extruder Controller firmware just sees a fluctuating temperature which may or may not trigger the firmware's error detection. A connection shorted closed, however, does tend to lead to detection of a problem. + +# Tool Action Commands + +#### 03 - Set toolhead target temperature + Unlike the Replicator, a value less than zero is accepted. + There is no max temp enforced. + +#### 13 - Enable/disable extra output + This command, also known as the open/close valve command, is intercepted by the Jetty Firmware and used to enable or disable acceleration in the planner. By default, when the accelerated stepper driver is used, acceleration is enabled. When a valve close command is seen, acceleration is disabled for the subsequent line segments to be printed. The planner plans them but with no acceleration steps. When a valve open command is seen, acceleration is enabled for the subsequent line segments: the planner plans them and uses acceleration in their planning. Using this mechanism, acceleration can be temporarily disabled and then later re-enabled. It is how the Skeinforge "Altshell" plugin causes perimeter shells to be printed without acceleration (and hence no artifacts caused by acceleration). + +#### 31 - Set build platform target temperature + Unlike the Replicator, a value less than zero is accepted. + There is no max temp enforced. + + diff --git a/firmware/SConstruct b/firmware/SConstruct index 9247733..33b0fe6 100644 --- a/firmware/SConstruct +++ b/firmware/SConstruct @@ -1,6 +1,6 @@ platform = ARGUMENTS.get('platform','mb24') -if (platform == 'rrmbv12' or platform == 'mb24' or platform == 'mb24-2560'): +if (platform == 'rrmbv12' or platform =='rrmbv12-2560' or platform == 'mb24' or platform == 'mb24-2560'): SConscript(['src/SConscript.motherboard'], variant_dir='build/'+platform) elif (platform == 'ecv22' or platform == 'ecv34'): diff --git a/firmware/simulator/Configuration.hh b/firmware/simulator/Configuration.hh new file mode 100644 index 0000000..f367b9b --- /dev/null +++ b/firmware/simulator/Configuration.hh @@ -0,0 +1,13 @@ +// Minimal Configuration.hh file for the planner simulator + +#ifndef SIMULATOR_CONFIGURATION_HH_ + +#define SIMULATOR_CONFIGURATION_HH_ + +// Need STEPPER_COUNT for Point.hh + +#ifndef STEPPER_COUNT +#define STEPPER_COUNT 5 +#endif + +#endif diff --git a/firmware/simulator/Makefile b/firmware/simulator/Makefile new file mode 100644 index 0000000..9ad90b3 --- /dev/null +++ b/firmware/simulator/Makefile @@ -0,0 +1,171 @@ +####### +# +# To add new executables for building look lower in this file for "EXE_TARGETS" +# +####### + +####### +# +# If the location of this directory is changed relative to the src/ +# directory OR if the src/shared/ or src/Motherboard/ directories +# are moved, then update the SRCDIR, SHAREDDIR, and MOTHERDIR variables +# + +# Relative path to G3Firmware/firmware/src + +SRCDIR = ../src + +# Relative paths to the shared sources and Motherboard sources + +SHAREDDIR = $(SRCDIR)/shared +MOTHERDIR = $(SRCDIR)/Motherboard +AVRFIXDIR = $(SHAREDDIR)/avrfix + +# +####### + +####### +# +# Since we need to compile sources from other directories, +# use make's VPATH functionality + +VPATH=./ $(SHAREDDIR) $(MOTHERDIR) $(AVRFIXDIR) + +# +####### + +AVRFIXFLAGS = -DMULKD -DSQRT -DCORDICHK -DROUNDKD -DDIVKD -DTEST_ON_PC + +####### +# +# OS/Platform dependencies -- deal with them here + +# Which platform? +BUILD_OS:=$(subst /,_,$(shell uname -s)) + +# Begin OS/Platform dependencies + +ifeq ($(BUILD_OS), Darwin) + +CXX = g++ +CXXFLAGS = -Wall -g -DSIMULATOR -I./ -I$(SHAREDDIR) -I$(MOTHERDIR) -I$(AVRFIXDIR) $(AVRFIXFLAGS) +CC = cc +CCFLAGS = -Wall -g -DSIMULATOR -I./ -I$(SHAREDDIR) -I$(MOTHERDIR) -I$(AVRFIXDIR) $(AVRFIXFLAGS) +LDFLAGS = -g -lm +OBJ = .o +DEP = .d +OBJDIR = DarwinObj +EXEDIR = $(OBJDIR) + +MAKE = make +MKDIR = mkdir -p +RMDIR = rm -rf + +endif + +ifeq ($(BUILD_OS), Linux) + +CXX = gcc +CXXFLAGS = -Wall -g -DSIMULATOR -I./ -I$(SHAREDDIR) -I$(MOTHERDIR) -I$(AVRFIXDIR) $(AVRFIXFLAGS) +CC = gcc +CCFLAGS = -Wall -g -DSIMULATOR -I./ -I$(SHAREDDIR) -I$(MOTHERDIR) -I$(AVRFIXDIR) $(AVRFIXFLAGS) +LDFLAGS = -g +OBJ = .o +DEP = .d +OBJDIR = LinuxObj +EXEDIR = $(OBJDIR) + +MAKE = make +MKDIR = mkdir -p +RMDIR = rm -rf + +endif + +# End OS/Platform dependencies (hopefully) +# +########## + +########## +# +# Add executables to build to the EXE_TARGETS variable +# +########## + +EXE_TARGETS = planner s3gdump + +########## +# +# For each executable to build, list its sources using the convention +# +# _SRCS = source-file-1 [source-file-2 ...] +# _OBJS = \ +# $(addprefix $(OBJDIR)/, $(notdir $(planner_SRCS:.cc=$(OBJ)))) +# +########## + +planner_DEFS = $(AVRFIXFLAGS) +planner_SRCS = planner.cc \ + StepperAccelPlannerExtras.cc \ + s3g.c \ + s3g_stdio.c \ + $(AVRFIXDIR)/avrfix.c \ + $(SHAREDDIR)/StepperAccelPlanner.cc \ + $(MOTHERDIR)/Point.cc +planner_LIBS = m + +planner_OBJS = $(notdir $(patsubst %.c,%$(OBJ),$(planner_SRCS:.cc=$(OBJ)))) + +#float_planner_DEFS = $(AVRFIXFLAGS) -Dnofixed +#float_planner_SRCS = planner.cc \ +# StepperAccelPlannerExtras.cc \ +# s3g.c \ +# s3g_stdio.c \ +# $(SHAREDDIR)/StepperAccelPlanner.cc \ +# $(MOTHERDIR)/Point.cc +# +#float_planner_OBJS = $(notdir $(patsubst %.c,%$(OBJ),$(planner_SRCS:.cc=$(OBJ)))) + +s3gdump_SRCS = s3gdump.c \ + s3g.c \ + s3g_stdio.c +s3gdump_OBJS = $(notdir $(s3gdump_SRCS:.c=$(OBJ))) +s3gdump_LIBS = m + +########## +# +# Everything from here on down is mundane +# +########## + +EXE_TARGET_OBJS = $(foreach objs, \ + $(addsuffix _OBJS, $(EXE_TARGETS)), \ + $(addprefix $(OBJDIR)/, $($(objs)))) + +LINK_TARGETS = $(addprefix $(OBJDIR)/, $(EXE_TARGETS)) + +all:: $(LINK_TARGETS) + +clean: + test -d $(OBJDIR) && $(RMDIR) $(OBJDIR) + +# Pull in auto-generated dependency information +-include $(wildcard $(OBJDIR)/*.d) + +$(LINK_TARGETS): $(EXE_TARGET_OBJS) + $(CXX) $(LDFLAGS) -o $@ \ + $(addprefix $(OBJDIR)/, $($(notdir $(addsuffix _OBJS, ${@})))) \ + $(addprefix -l, $($(notdir $(addsuffix _LIBS, ${@})))) + +# Really should have variables for the dependency switches, but +# there's only so many hours in the day +$(OBJDIR)/%$(OBJ): %.cc + test -d $(OBJDIR) || $(MKDIR) $(OBJDIR) + $(CXX) $(CXXFLAGS) $($(notdir $(addsuffix _DEFS, $(basename ${@})))) -c -o $@ $< + $(CXX) $(CXXFLAGS) $($(notdir $(addsuffix _DEFS, $(basename ${@})))) \ + -MM -MF $(OBJDIR)/$*$(DEP) -MT $(OBJDIR)/$*$(OBJ) $(CXXFLAGS) $< + +$(OBJDIR)/%$(OBJ): %.c + test -d $(OBJDIR) || $(MKDIR) $(OBJDIR) + $(CC) $(CCFLAGS) $($(notdir $(addsuffix _DEFS, $(basename ${@})))) -c -o $@ $< + $(CC) $(CCFLAGS) $($(notdir $(addsuffix _DEFS, $(basename ${@})))) \ + -MM -MF $(OBJDIR)/$*$(DEP) -MT $(OBJDIR)/$*$(OBJ) $(CXXFLAGS) $< diff --git a/firmware/simulator/Simulator.hh b/firmware/simulator/Simulator.hh new file mode 100644 index 0000000..2d40dbd --- /dev/null +++ b/firmware/simulator/Simulator.hh @@ -0,0 +1,27 @@ +// Simulator.hh +// Handle porting issues between avr-gcc and gcc + +#ifndef SIMULATOR_HH_ + +#define SIMULATOR_HH + +#include + +// avr-gcc makes double the same as float +#define double float + +// Maybe at some point in the future, we'll want to replace these +// with pthread mutices. That, if it becomes desirable to simulate +// interrupts pulling information out of the pipeline: use a thread +// to simulate an interrupt and have the planner running in the +// primal thread, possibly at a lower thread priority. + +#define CRITICAL_SECTION_START {} +#define CRITICAL_SECTION_END {} + +// Seems like a good idea, eh? +#ifndef HAS_STEPPER_ACCELERATION +#define HAS_STEPPER_ACCELERATION +#endif + +#endif diff --git a/firmware/simulator/SimulatorRecord.hh b/firmware/simulator/SimulatorRecord.hh new file mode 100644 index 0000000..2fea0e4 --- /dev/null +++ b/firmware/simulator/SimulatorRecord.hh @@ -0,0 +1,50 @@ +#ifndef SIMULATORRECORD_HH_ + +#define SIMULATORRECORD_HH_ + +// Item codes for use with plan_record() +#define RECORD_ADD 1 // Record an addition or subtraction op +#define RECORD_MUL 2 // Record a multiplication op +#define RECORD_DIV 3 // Record a division op +#define RECORD_SQRT 4 // Record a square root op +#define RECORD_CALC 5 // Record a calculation op +#define RECORD_RECALC 6 // Record a re-calculation op + +// This macro is used in StepperAccelPlanner.cc to record +// operations. When SIMULATOR is defined, it actually calls +// down to plan_record(). Otherwise, it is a no-op as set +// in StepperAccelPlanner.hh + +#ifdef SIMULATOR_RECORD +#undef SIMULATOR_RECORD +#endif + +#define SIMULATOR_RECORD(x...) plan_record(NULL, ## x, 0) + +// void plan_record(void *ctx, int item_code, ...) +// +// Record an operation such as an addition or multiply. Intended usage is +// +// plan_record(ctx, item_code_1, count_1 [, item_code_2, count_2, ...], 0); +// +// where each item code is one of the RECORD_ constants such as RECORD_ADD +// which names an operation to record, and count is the number of occurrences +// of that operation. A final value of 0 should be used to terminate the +// list of item codes and counts. +// +// Call arguments: +// +// void *ctx +// Reserved for future use. A placeholder for now. +// +// int item_code +// The type of operation to record. Must be one of the RECORD_ +// constants. Each RECORD_ item code must be followed by a single +// integer value of type "int". The list must be terminated by a +// final int argument of value 0. +// +// Return values: none + +extern void plan_record(void *ctx, int item_code, ...); + +#endif diff --git a/firmware/simulator/StepperAccelPlannerExtras.cc b/firmware/simulator/StepperAccelPlannerExtras.cc new file mode 100644 index 0000000..47ef24c --- /dev/null +++ b/firmware/simulator/StepperAccelPlannerExtras.cc @@ -0,0 +1,1190 @@ +// Stepperaccelplannerextrasquer.cc +// +// This module fills two roles +// 1. Provide no-op stubs for routines needed by StepperAccelPlanner, and +// 2. Provide additional utility routines needed by the simulated planner + +#include +#include +#include +#include +#include + +#include "Simulator.hh" +#include "StepperAccelPlanner.hh" +#include "StepperAccelPlannerExtras.hh" +#include "SqrtTable.hh" + +// Which master_steps to use when calculating the feed rate +#define MASTER_STEPS planner_master_steps_cfr + +//#define INCREMENTAL_TIME + +#define min(a,b) (((a)<=(b))?(a):(b)) +#define max(a,b) (((a)>=(b))?(a):(b)) + +uint32_t simulator_debug = false; +bool simulator_use_max_feed_rate = false; +FPTYPE simulator_max_feed_rate = 0; +bool simulator_dump_speeds = false; +bool simulator_show_alt_feed_rate = false; + +uint32_t z1[10000]; +uint32_t z2[10000]; +int iz = 0; + +// From time to time, StepperAccelPlanner.cc wants these for debugging +volatile float zadvance, zadvance2; + +// These values are only needed for the "ADVANCE" feature of the planner +static FPTYPE advanceK = 0; +static FPTYPE advanceK2 = 0; +static FPTYPE noodleDiameter = 0; +//static float axis_steps_per_unit_e = 4.4; + +extern float axis_steps_per_unit[NUM_AXIS]; +FPTYPE axis_steps_per_unit_inverse[NUM_AXIS]; + +// Bins for tallying up how many blocks are planned once, twice, thrice, ... +// A block cannot be planned more time than there are blocks in the pipe line +static int planner_counts[BLOCK_BUFFER_SIZE+1]; + +// Track total time required to print +static float total_time = 0.0; + +// Storage for the plan_record() counters +static int record_add = 0; +static int record_mul = 0; +static int record_div = 0; +static int record_sqrt = 0; +static int record_calc = 0; +static int record_recalc = 0; + +// Show calls to plan_buffer_() +static int show_calls = 0; + +// Segment acceleration state +static bool segmentAccelState = true; + +// We track the last absolute coordinate seen so that setTargetNew() can +// convert absolute coordinates to the relative coordinates needed by the planner +static Point lastTarget(0, 0, 0, 0, 0); +static Point droppedSegmentsRelative(0, 0, 0, 0, 0); +static int32_t droppedSegmentsUs = 0; + +// Stubbs for routines used by StepperAccelPlanner +// While we could #ifdef the usage of these routines away in StepperAccelPlanner +// itself, it's just as easy to make our own versions of them. That way we save +// some #ifdef clutter from occurring in StepperAccelPlanner + +// Note, we draw the line at providing stubb classes such as Motherboard and +// StepperInterface. For those, we #ifdef the usage away in StepperAccelPlanner + +void st_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e) { } +void st_set_e_position(const int32_t &e) { } +int32_t st_get_position(uint8_t axis) { return 0; } +void st_wake_up(void) { } + +// Yet another occurrence of this routine + +// Given two coordinates and a time interval to traverse that +// distance between those two coordinates, compute the corresponding +// feed rate. The time interval uses units of microseconds while +// the points are specified in stepper-space. + +FPTYPE calcFeedRate(const Point& from, const Point& to, int32_t interval, bool includeEAxis, uint32_t us, + int32_t& zsteps) +{ + //Calculate the distance in mm's by: + //Calculate the delta distances and convert to mm's + //Then sqr of sum of squares of the deltas for the distance + + //We also calculate at the same time, planner_master_steps in steps by finding the dominant axis (for all 5) + //You would think it would be for X/Y/Z only, but they "mistakenly" use all 5 in rep g. + //so we do the same here for consistancy + + // planner_distance_cfr -- May or may not include the A & B axes + // planner_distance -- Includes ALL axes + +#ifdef FIXED + FPTYPE master_delta ; // Initialized later + uint8_t master_delta_index; // Initialized later + FPTYPE master_delta_cfr = 0; + uint8_t master_delta_index_cfr = 0; + float d = 0.0; + float d_cfr = 0.0; +#endif + + // Handle the X, Y, and Z axes + + bool override_master_steps = false; + uint32_t planner_master_steps_cfr = 0; + FPTYPE planner_distance_cfr = 0; + + zsteps = 0; + +#define MAX_STEPS 0x7000 +// For testing on a ToM, try a MAX_STEPS which will trigger at 4 cm of Z axis travel @ 200 steps/mm +//#define MAX_STEPS ( 40 * 200 ) + + // Recall that X_AXIS, Y_AXIS, Z_AXIS are the actual indices + for ( uint8_t i = X_AXIS; i <= Z_AXIS; i++ ) + { + planner_steps[i] = to[i] - from[i]; + if ( planner_steps[i] != 0 ) + { + if ( planner_steps[i] > MAX_STEPS ) + { + if ( i == Z_AXIS ) + { + // We are doing a large travel on a high resolution Z axis + // Let's break this move into several smaller moves. We don't + // actually do this "correctly" as we take all the X, Y, and E + // steps in the first submove. Thus this is a hack intended + // only for large Z-axis travel situations. + + // Unexercised Z-axis steps which will remain once we truncate + // this move to MAX_STEPS steps + + zsteps = planner_steps[Z_AXIS] - MAX_STEPS; + } + planner_steps[i] = MAX_STEPS; + } + else if ( planner_steps[i] < -(MAX_STEPS) ) + { + // This is so infrequent, that we'll take the float hit + // Besides, we're dealing with a value to large for an _Accum + if ( i == Z_AXIS ) + { + // We are doing a large travel on a high resolution Z axis + // Let's break this move into several smaller moves. We don't + // actually do this "correctly" as we take all the X, Y, and E + // steps in the first submove. Thus this is a hack intended + // only for large Z-axis travel situations. + + // Unexercised Z-axis steps which will remain once we truncate + // this move to MAX_STEPS steps + + zsteps = planner_steps[Z_AXIS] + MAX_STEPS; + } + planner_steps[i] = -(MAX_STEPS); + } + + delta_mm[i] = FPMULT2(ITOFP(planner_steps[i]), axis_steps_per_unit_inverse[i]); + planner_steps[i] = labs(planner_steps[i]); +#ifdef FIXED + if ( FPABS(delta_mm[i]) > master_delta_cfr ) + { + master_delta_index_cfr = i; + master_delta_cfr = FPABS(delta_mm[i]); + } +#else + planner_distance_cfr += FPSQUARE(delta_mm[i]); +#endif + if ( (uint32_t)planner_steps[i] > planner_master_steps_cfr ) + planner_master_steps_cfr = (uint32_t)planner_steps[i]; + float dd = FPTOF(delta_mm[i]); + d_cfr += dd * dd; + } + else + delta_mm[i] = 0; + } + + // For an extruder only move, force includeEAxis to be true + if ( planner_master_steps_cfr == 0 ) + { + includeEAxis = true; + override_master_steps = true; + } + + // Handle the extruder axes + planner_master_steps = planner_master_steps_cfr; +#ifdef FIXED + master_delta_index = master_delta_index_cfr; + master_delta = master_delta_cfr; + d = d_cfr; +#else + planner_distance = planner_distance_cfr; +#endif + + for ( uint8_t i = Z_AXIS+1; i < NUM_AXIS; i++ ) + { + planner_steps[i] = to[i] - from[i]; + if ( planner_steps[i] != 0 ) + { + delta_mm[i] = FPMULT2(ITOFP(planner_steps[i]), axis_steps_per_unit_inverse[i]); + planner_steps[i] = labs(planner_steps[i]); +#ifdef FIXED + if ( FPABS(delta_mm[i]) > master_delta ) + { + master_delta_index = i; + master_delta = FPABS(delta_mm[i]); + } +#else + planner_distance += FPSQUARE(delta_mm[i]); +#endif + if ( (uint32_t)planner_steps[i] > planner_master_steps ) + planner_master_steps = (uint32_t)planner_steps[i]; + float dd = FPTOF(delta_mm[i]); + d += dd * dd; + } + else + delta_mm[i] = 0; + } + + // If we forced includeEAxis on (because this is an extruder only move) then + // also use planner_master_steps for planner_master_steps_cfr (which is zero) + + if ( override_master_steps ) planner_master_steps_cfr = planner_master_steps; + + // planner_distance is now a value between 0 and 3. We want to know + // + // sqrt(1+planner_distance)*delta_mm[master_delta_index] + // + // Our lookup table will give us table[(int)(x * SQRT_TABLE_RESOLUTION)] = sqrt(1 + x), 0 <= x <= 3 + + if ( includeEAxis ) + { + // All distances include the A & B axes + // planner_distance_cfr == planner_distance + // Just compute planner_distance and then set planner_distance_cfr = planner_distance +#ifdef FIXED + FPTYPE planner_distance_sq = 0; + for ( uint8_t i = 0; i < NUM_AXIS; i++ ) + { + if ( (i == master_delta_index) || (delta_mm[i] == 0) ) continue; + planner_distance_sq += FPSQUARE(FPDIV(delta_mm[i], master_delta)); + } + + uint8_t j = FPTOI(planner_distance_sq << SQRT_TABLE_SHIFT); + planner_distance = FPMULT2(sqrt_table[j], master_delta); +#else + planner_distance = FPSQRT(planner_distance); +#endif + planner_distance_cfr = planner_distance; + d_cfr = d; + } + else + { + // We want planner_distance_cfr to NOT include the A or B axes (E_AXIS) +#ifdef FIXED + FPTYPE planner_distance_sq; + + // First tackle planner_distance_cfr + + // The (x,y,z) calc + planner_distance_sq = 0; + for ( uint8_t i = X_AXIS; i <= Z_AXIS; i++ ) + { + if ( (i == master_delta_index_cfr) || (delta_mm[i] == 0) ) continue; + planner_distance_sq += FPSQUARE(FPDIV(delta_mm[i], master_delta_cfr)); + } + + uint8_t j = FPTOI(planner_distance_sq << SQRT_TABLE_SHIFT); + planner_distance_cfr = FPMULT2(sqrt_table[j], master_delta_cfr); + + // Now include the A & B axes for planner_distance + // If master_index == master_index_cfr then we can use the prior calcs as a starting point + // Also master_delta == master_delta_cfr (which is significant when we get the the square root calc + + if ( master_delta_index == master_delta_index_cfr ) + { + for ( uint8_t i = Z_AXIS+1; i < NUM_AXIS; i++ ) + { + if ( (i == master_delta_index_cfr) || (delta_mm[i] == 0) ) continue; + planner_distance_sq += FPSQUARE(FPDIV(delta_mm[i], master_delta_cfr)); + } + } + else + { + // master_index != master_index_cfr + // master_delta != master_delta_cfr + + // This means that the longest distance component was the A or B axis + // We need to recompute the sum of the squares with a different normalization + + planner_distance_sq = 0; + for ( uint8_t i = 0; i < NUM_AXIS; i++ ) { + if ( (i == master_delta_index) || (delta_mm[i] == 0) ) continue; + planner_distance_sq += FPSQUARE(FPDIV(delta_mm[i], master_delta)); + } + } + + j = FPTOI(planner_distance_sq << SQRT_TABLE_SHIFT); + planner_distance = FPMULT2(sqrt_table[j], master_delta); +#else + planner_distance_cfr = FPSQRT(planner_distance_cfr); + planner_distance = FPSQRT(planner_distance); +#endif + } + +// It's bad news if we get to here without MASTER_STEPS being defined +#ifndef MASTER_STEPS +#define MASTER_STEPS planner_master_steps_cfr +#endif + +#ifdef FIXED + d = sqrt(d); + d_cfr = sqrt(d_cfr); + + if ((fabsf(FPTOF(planner_distance) - d)/d) > 0.01) + printf("*** calcFeedRate([%d,%d,%d,%d],[%d,%d,%d,%d],%d,%d): planner_distance = %f; actual distance = %f\n", + from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], interval, includeEAxis ? 1 : 0, + FPTOF(planner_distance), d); + + if ((fabsf(FPTOF(planner_distance_cfr) - d_cfr)/d_cfr) > 0.01) + printf("*** calcFeedRate([%d,%d,%d,%d],[%d,%d,%d,%d],%d,%d): planner_distance_cfr = %f; actual distance_cfr = %f\n", + from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], interval, includeEAxis ? 1 : 0, + FPTOF(planner_distance_cfr), d_cfr); + + float feed_rate = ((interval != 0) && (MASTER_STEPS != 0)) ? + (d_cfr * 1000000.0) / ((float)interval * (float)MASTER_STEPS) : 0; + FPTYPE result; + if ( (interval <= 0) || (MASTER_STEPS == 0) ) result = 0; + // if (interval >= 0 && interval <= 0x7fff && MASTER_STEPS <= 0x7fff) + else if ( (((uint32_t)interval | (uint32_t)MASTER_STEPS) & 0xffff8000) == 0 ) { + + // We can convert interval to an _Accum and then shift it entirely to the right of the + // fixed decimal point without any loss of precision. This ammounts to dividing + // interval by 2^16. At the same time, we multiply planner_distance by 1000000 / 2^16, + // again with no loss of precision. Since we've divided both the numerator and denominator + // by 2^16, we still get the correct result. + + // This code case is expected to be the predominant case -- we want it to be fast + + result = FPDIV(FPMULT2(planner_distance_cfr, KCONSTANT_1000000_LSR_16), + FPMULT2(ITOFP((int32_t)MASTER_STEPS), (ITOFP((int32_t)interval)>>16))); + } + // else if (interval >= 0 && interval <= 0x7fffff && MASTER_STEPS <= 0x7fffff) + else if ( (((uint32_t)interval | (uint32_t)MASTER_STEPS) & 0xff800000) == 0 ) + { + // interval or MASTER_STEPS (or both) are too large to play the trick + // we did above. Their product may still overflow an _Accum. So, we shift each + // one over until it is <= 0x7fff, keeping track of how many bits we shifted. + // Then we do the same trick as above but shift by only >> (16-i) where i is the + // number of bits we pre-shifted interval and MASTER_STEPS by. + + // For this case we do have loss of precision, but it should be pretty minor as these + // are large numbers. + + // This case should be an infrequent case, likely only happening for large rafts or + // other very large, slow moves. As such, the code being a little slower here should + // not be too much of a problem as the printer is moving slow which allows the pipeline + // planner to be slower as well. + + // Of course, a machine with LOTS of steps/mm for a given axes might generate large + // MASTER_STEPS. + + uint8_t n = 16; + while (interval > 0x7fff) + { + interval >>= 1; + n--; + } + while (MASTER_STEPS > 0x7fff) + { + MASTER_STEPS >>= 1; + n--; + } + + // Since both interval and MASTER_STEPS were <= 0x7fffff + // when we started, we know that n >= 0. In fact, it's >= 2. + // At any rate, we won't do ">> (negative-number)". + + // So, yes we could have started with interval <= 0xffffff and MASTER_STEPS <= 0xffffff + + result = FPDIV(FPMULT2(planner_distance_cfr, KCONSTANT_1000000_LSR_16), + FPMULT2(ITOFP((int32_t)MASTER_STEPS), (ITOFP((int32_t)interval)>>n))); + } + else + { + // interval or MASTER_STEPS or both are really large numbers or interval < 0 + // Just go ahead and pay the penalty of conversion to and from floats + + // This case is not expected to happen in practice + + result = FTOFP(((FPTOF(planner_distance_cfr) * 1000000.0) / + ((float)MASTER_STEPS * (float)interval))); + + printf("*** calcFeedRate([%d,%d,%d,%d],[%d,%d,%d,%d],%d,%d): FLOAT: using floating point math; " + "interval=%d, master_steps=%u\n", + from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], interval, includeEAxis ? 1 : 0, + interval, MASTER_STEPS); + } + if ((us != 0) && simulator_show_alt_feed_rate) + { + float alt_feed_rate = d_cfr*1000.0*1000.0/(float)us; + if ((alt_feed_rate != 0.0) && ((fabsf(feed_rate - alt_feed_rate)/alt_feed_rate) > 0.01)) + printf("*** calcFeedRate([%d,%d,%d,%d],[%d,%d,%d,%d],%d,%d): us=%u, d(xyz)=%f, alt feed rate=%f, " + "float feed rate=%f\n", + from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], interval, includeEAxis ? 1 : 0, + us, d_cfr, alt_feed_rate, feed_rate); + } + if (simulator_debug & DEBUG_FEEDRATE) + printf("*** calcFeedRate([%d,%d,%d,%d],[%d,%d,%d,%d],%d,%d): DEBUG: master_steps=%u, fixed d=%f, float d=%f, " + "fixed feed rate=%f, float feed rate=%f\n", + from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], interval, includeEAxis ? 1 : 0, + MASTER_STEPS, FPTOF(planner_distance), d, FPTOF(result), feed_rate); + else if ((feed_rate != 0.0) && ((fabsf(feed_rate - FPTOF(result))/feed_rate) > 0.01)) + // Different output from the above so as to not cause differences with old, saved output files + // which we may perform "diff's against + printf("*** calcFeedRate([%d,%d,%d,%d],[%d,%d,%d,%d],%d,%d): error > 1%%: interval=%d, master_steps=%u, " + "fixed feed rate=%f; float feed rate=%f\n", + from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], interval, includeEAxis ? 1 : 0, + interval, MASTER_STEPS, FPTOF(result), feed_rate); + if ( simulator_use_max_feed_rate && result > simulator_max_feed_rate ) + printf("*** calcFeedRate([%d,%d,%d,%d],[%d,%d,%d,%d],%d,%d): rate > %4.1f: master_steps=%u, d=%f, d_cfr=%f, " + "fixed feed rate=%f; float feed rate=%f\n", + from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], interval, includeEAxis ? 1 : 0, + FPTOF(simulator_max_feed_rate), MASTER_STEPS, d, d_cfr, FPTOF(result), feed_rate); + + return result; +#else + planner_distance = FPSQRT(planner_distance); + return ((planner_distance * 1000000.0) / ((FPTYPE)interval * (FPTYPE)MASTER_STEPS)); +#endif +} + + +// And yet another occurrence of this routine + +// The x, y, z, and e arguments are from a .s3g file or frame +// and are expressed in stepper space. Whether they are absolute +// or relative coordinates is denoted by bits within the "relative" +// call argument. The "us" argument specifies how many microseconds +// to take in travelling to (x,y,z,e) from the prior position. + +void setTargetNew(int32_t x, int32_t y, int32_t z, int32_t a, int32_t b, int32_t us, int8_t relative) +{ + Point target(x, y, z, a, b); + Point newPosition = target; + + for (int i = 0; i < AXIS_COUNT; i++) + { + if ((relative & (1 << i)) != 0) + { + newPosition[i] = lastTarget[i] + target[i]; + + //Added on any relative move we have saved from a previous dropped segments + //Only if we're relative, if we're absolute we don't care + newPosition[i] += droppedSegmentsRelative[i]; + } + } + us += droppedSegmentsUs; + + int32_t max_delta = 0; + for (int i = 0; i < AXIS_COUNT; i++) + { + // Do not include extruder steps in max_delta unless this is an extruder-only move + // More correct code would allow for two extruders. However, it's not as simple + // as "i >= E_AXIS" as that would only pick up the first extruder e-steps and not + // the larger of the two extruder e-steps. + if (i >= E_AXIS && max_delta != 0) continue; + int32_t delta = newPosition[i] - lastTarget[i]; + if ( delta < 0 ) delta *= -1; + if ( delta > max_delta ) + max_delta = delta; + } + + if (max_delta != 0) + { + int32_t dda_interval = us / max_delta; + int32_t zsteps; + do + { + int32_t ztarget; + FPTYPE feedRate = calcFeedRate(lastTarget, newPosition, dda_interval, false, us, zsteps); + if (show_calls) + printf("plan_buffer_line(%4d, %4d, %4d, %4d, %7.2f, 0)\n", + newPosition[0], newPosition[1], newPosition[2], + newPosition[3], FPTOF(feedRate)); + if ( zsteps != 0 ) + { + // In this case, plan_buffer_line() is assured to return True + // since the Z-axis motion exceeds 0x7000 steps + ztarget = newPosition[2] - zsteps; + } + else + ztarget = newPosition[2]; + if (plan_buffer_line(newPosition[0], newPosition[1], ztarget, + newPosition[3], feedRate, 0, segmentAccelState)) + { + lastTarget = newPosition; + droppedSegmentsRelative = Point(0, 0, 0, 0, 0); + droppedSegmentsUs = 0; + if ( zsteps != 0 ) + // Actually it's okay to always do this step; however, it may be slower to always do it + // so we only do it when r.zsteps != 0 + lastTarget[2] = ztarget; + } + else + { + //Because we've dropped segments, we need to add on a "relative" version + //of the dropped move onto droppedSegmentsRelative for possible later use, if we + //do another relative move for that axis, otherwise relative moves can be lost. + //Relative moves are added to the existing value, absolute moves have lastTarget + //subtracted. + //All information is stored in "relative" co-ords +#if 0 + printf("*** setTargetNew(): plan_buffer_line(%4d, %4d, %4d, %4d, %7.2f, 0) " + "dropped segment\n", + newPosition[0], newPosition[1], newPosition[2], + newPosition[3], FPTOF(feedRate)); +#endif + for (uint8_t i = 0; i < AXIS_COUNT; i ++) + { + if ((relative & (1 << i)) != 0) + droppedSegmentsRelative[i] += target[i]; + else + droppedSegmentsRelative[i] = target[i] - lastTarget[i]; + } + droppedSegmentsUs += us; + } + } while ( zsteps != 0 ); + } +} + +void setTarget(int32_t x, int32_t y, int32_t z, int32_t a, int32_t b, int32_t dda_interval) +{ + Point target(x, y, z, a, b); + int32_t zsteps, ztarget; + + do + { + FPTYPE feedRate = calcFeedRate(lastTarget, target, dda_interval, true, 0, zsteps); + if ( zsteps != 0 ) + { + // In this case, plan_buffer_line() is assured to return True + // since the Z-axis motion exceeds 0x7000 steps + ztarget = target[2] - zsteps; + } + else + ztarget = target[2]; + if (show_calls) + printf("plan_buffer_line(%4d, %4d, %4d, %4d, %7.2f, 0)\n", + target[0], target[1], ztarget, target[3], FPTOF(feedRate)); + if (plan_buffer_line(target[0], target[1], ztarget, target[3], feedRate, 0, segmentAccelState)) + lastTarget = target; + if ( zsteps != 0 ) + lastTarget[2] = ztarget; + } while ( zsteps != 0 ); +} + +void definePosition(int32_t x, int32_t y, int32_t z, int32_t a, int32_t b) +{ + Point target(x, y, z, a, 0); + if (show_calls) + printf("plan_set_position(%4d, %4d, %4d, %4d, %4d)\n", x, y, z, a, b); + plan_set_position(x, y, z, a); + lastTarget = target; +} + +void setSegmentAccelState(bool state) +{ + segmentAccelState = state; +} + + +// Set acceleration parameters used by the planner and normally +// stored in EEPROM. These values should probably be moved to +// a header file at which time the Makefile should learn about +// header file dependencies. + +// This routine also calls plan_init() + +void plan_init_eeprom(void) +{ + int i; + + max_feedrate[X_AXIS] = FTOFP((float)160.0); + max_feedrate[Y_AXIS] = FTOFP((float)160.0); + max_feedrate[Z_AXIS] = FTOFP((float)16.0); + max_feedrate[E_AXIS] = FTOFP((float)100.0); + + axis_steps_per_unit[X_AXIS] = 47.069852; + axis_steps_per_unit[Y_AXIS] = 47.069852; + axis_steps_per_unit[Z_AXIS] = 200.0; + axis_steps_per_unit[E_AXIS] = 4.4; + + max_acceleration_units_per_sq_second[X_AXIS] = 500; + max_acceleration_units_per_sq_second[Y_AXIS] = 500; + max_acceleration_units_per_sq_second[Z_AXIS] = 150; + max_acceleration_units_per_sq_second[E_AXIS] = 60000; + + for (i = 0; i < 5; i++) + { + axis_steps_per_unit_inverse[i] = FTOFP(1.0 / axis_steps_per_unit[i]); + axis_steps_per_sqr_second[i] = (uint32_t)((float)max_acceleration_units_per_sq_second[i] + * axis_steps_per_unit[i]); + } + + p_acceleration = FTOFP((float)2000.0); + p_retract_acceleration = FTOFP((float)4000.0); + + minimumfeedrate = FTOFP((float)0.0); + mintravelfeedrate = FTOFP((float)0.0); + + minimumPlannerSpeed = FTOFP((float)2.0); + + advanceK = FTOFP((float)0.00850); +#ifdef JKN_ADVANCE + advanceK2 = FTOFP((float)0.00900); +#endif + + noodleDiameter = FTOFP((float)0.58); + minimumSegmentTime = FTOFP((float)0.02); + extruder_only_max_feedrate = FTOFP(200.0); + + // extruder_deprime_steps = (int32_t)(axis_steps_per_unit[E_AXIS] * 0.0); + slowdown_limit = 4; + // clockwise_extruder = 1; + + max_speed_change[X_AXIS] = FTOFP((float)30.0); + max_speed_change[Y_AXIS] = FTOFP((float)30.0); + max_speed_change[Z_AXIS] = FTOFP((float)10.0); + max_speed_change[E_AXIS] = FTOFP((float)30.0); + +#ifdef YET_ANOTHER_JERK + smallest_max_speed_change = max_speed_change[Z_AXIS]; + for (i = 0; i < NUM_AXIS; i++) + if (max_speed_change[i] < smallest_max_speed_change) + smallest_max_speed_change = max_speed_change[i]; +#endif + + plan_init(advanceK, advanceK2, 0); + + lastTarget = Point(0, 0, 0, 0, 0); + droppedSegmentsRelative = Point(0, 0, 0, 0, 0); + droppedSegmentsUs = 0; + + memset(planner_counts, 0, sizeof(planner_counts)); + total_time = 0.0; + + setSegmentAccelState(true); +} + + +void plan_record(void *ctx, int item_code, ...) +{ + va_list ap; + + (void)ctx; + + va_start(ap, item_code); + while (item_code != 0) + { + switch(item_code) + { + case RECORD_CALC: + record_calc += va_arg(ap, int); + break; + + case RECORD_ADD: + record_add += va_arg(ap, int); + break; + + case RECORD_MUL: + record_mul += va_arg(ap, int); + break; + + case RECORD_DIV: + record_div += va_arg(ap, int); + break; + + case RECORD_SQRT: + record_sqrt += va_arg(ap, int); + break; + + case RECORD_RECALC: + record_recalc += va_arg(ap, int); + break; + + default : + goto badness; + } + item_code = va_arg(ap, int); + } +badness: + va_end(ap); +} + +extern volatile unsigned char block_buffer_head; // Index of the next block to be pushed +extern volatile unsigned char block_buffer_tail; // Index of the block to process now + +#define MAX_STEP_FREQUENCY 40000 +static uint16_t calc_timer(uint16_t step_rate, int *step_loops) +{ + if (step_rate > MAX_STEP_FREQUENCY) + step_rate = MAX_STEP_FREQUENCY; + + if (step_rate > 20000) + { + // If steprate > 20kHz >> step 4 times + step_rate = (step_rate >> 2)&0x3fff; + *step_loops = 4; + } + else if(step_rate > 10000) + { + // If steprate > 10kHz >> step 2 times + step_rate = (step_rate >> 1)&0x7fff; + *step_loops = 2; + } + else + *step_loops = 1; + + if (step_rate < 32) + step_rate = 32; + + return (uint16_t)((uint32_t)2000000 / (uint32_t)step_rate); +} + +void plan_dump_current_block(int discard) +{ + int32_t acceleration_time, coast_time, deceleration_time; + uint16_t acc_step_rate, dec_step_rate, intermed; + char action[5]; + block_t *block; + int count_direction[NUM_AXIS], step_loops; + uint8_t out_bits; + uint32_t step_events_completed; + static int i = 0; + static float z_height = 10.0; // figure z-offset is around 10 +#ifdef INCREMENTAL_TIME + uint32_t acc_step_rate_32; + uint16_t acc_step_rate_rem, dec_step_rate_rem; + uint16_t atimer, dtimer; +#else + uint16_t timer; +#endif + + block = plan_get_current_block(); + if (!block) + return; + + if (block->acceleration_rate == 0) + return; + + action[0] = (block->steps[X_AXIS] != 0) ? + (((uint32_t)(0x7fffffff & block->steps[X_AXIS]) == block->step_event_count) ? 'X' : 'x') : ' '; + action[1] = (block->steps[Y_AXIS] != 0) ? + (((uint32_t)(0x7fffffff & block->steps[Y_AXIS]) == block->step_event_count) ? 'Y' : 'y') : ' '; + action[2] = (block->steps[Z_AXIS] != 0) ? + (((uint32_t)(0x7fffffff & block->steps[Z_AXIS]) == block->step_event_count) ? 'Z' : 'z') : ' '; + action[3] = (block->steps[E_AXIS] != 0) ? + (((uint32_t)(0x7fffffff & block->steps[E_AXIS]) == block->step_event_count) ? 'E' : 'e') : ' '; + action[4] = '\0'; + +#ifndef INCREMENTAL_TIME + acc_step_rate = block->initial_rate; + acceleration_time = calc_timer(acc_step_rate, &step_loops); + + dec_step_rate = 0; + deceleration_time = 0; + + coast_time = 0; + step_events_completed = 0; + intermed = 0; + + for (step_events_completed = step_loops; + step_events_completed <= block->step_event_count; ) + { + if (step_events_completed <= (uint32_t)(0x7fffffff & block->accelerate_until)) + { + // speed(t) = speed(0) + acceleration * t + step_events_completed += step_loops; + uint16_t old_acc_step_rate = acc_step_rate; + uint16_t intermed_a; + acc_step_rate = intermed_a = + (uint16_t)((0xffffffffff & ((uint64_t)(0x00ffffff & acceleration_time) * + (uint64_t)(0x00ffffff & block->acceleration_rate))) >> 24); + acc_step_rate += block->initial_rate; + if (acc_step_rate < old_acc_step_rate) + printf("*** While accelerating, the step rate overflowed: " + "acc_step_rate = %u = %u + %u = %u + 0x%x * 0x%x\n", + acc_step_rate, block->initial_rate, + intermed_a, block->initial_rate, + block->acceleration_rate, + acceleration_time); + if (acc_step_rate > block->nominal_rate) + acc_step_rate = block->nominal_rate; + acceleration_time += timer = calc_timer(acc_step_rate, &step_loops); + dec_step_rate = acc_step_rate; + } + else if (step_events_completed > (uint32_t)(0x7fffffff & block->decelerate_after)) + { + // speed(t) = speed(0) - deceleration * t + step_events_completed += step_loops; + uint16_t old_intermed = intermed; + intermed = + (uint16_t)((0xffffffffff & ((uint64_t)(0x00ffffff & deceleration_time) * + (uint64_t)(0x00ffffff & block->acceleration_rate))) >> 24); + if (intermed > acc_step_rate) + dec_step_rate = block->final_rate; + else + dec_step_rate = acc_step_rate - intermed; + if (dec_step_rate < block->final_rate) + dec_step_rate = block->final_rate; + if (intermed < old_intermed) + printf("*** While decelerating, the step rate overflowed: " + "%u = %u - %u = %u - 0x%x * 0x%x\n", + dec_step_rate, acc_step_rate, intermed, + acc_step_rate, block->acceleration_rate, + deceleration_time); + deceleration_time += calc_timer(dec_step_rate, &step_loops); + } + else + { + // Must make this call as it has side effects + step_events_completed += step_loops; + coast_time += calc_timer(acc_step_rate, &step_loops); + dec_step_rate = acc_step_rate; + } + } + +#else + + acc_step_rate = block->initial_rate; + acceleration_time = atimer = calc_timer(acc_step_rate, &step_loops); + acc_step_rate_rem = 0; + + deceleration_time = dtimer = 0; + dec_step_rate = 0; + dec_step_rate_rem = 0; + + coast_time = 0; + + for (step_events_completed = step_loops; + step_events_completed <= block->step_event_count; ) + { + if (step_events_completed <= (uint32_t)(0x7fffffff & block->accelerate_until)) + { + step_events_completed += step_loops; + + // speed(t) = speed(0) + acceleration * t + uint16_t old_acc_step_rate = acc_step_rate; + acc_step_rate_rem += (uint32_t)(((uint64_t)atimer * (uint64_t)(0xffffff & block->acceleration_rate)) >> 16); + acc_step_rate += acc_step_rate_rem >> 8; + acc_step_rate_rem &= 0xff; + if (acc_step_rate < old_acc_step_rate) + printf("*** While accelerating, the step rate overflowed: " + "acc_step_rate = %u; acc_step_rate_rem = %u\n", + acc_step_rate, acc_step_rate_rem); + if (acc_step_rate > block->nominal_rate) + acc_step_rate = block->nominal_rate; + atimer = calc_timer(acc_step_rate, &step_loops); + acceleration_time += atimer; + dec_step_rate = acc_step_rate; + } + else if (step_events_completed > (uint32_t)(0x7fffffff & block->decelerate_after)) + { + // speed(t) = speed(0) - deceleration * t + step_events_completed += step_loops; + uint16_t old_dec_step_rate = dec_step_rate; + dec_step_rate_rem += (uint32_t)(((uint64_t)dtimer * (uint64_t)(0xffffff & block->acceleration_rate)) >> 16); + uint16_t foo = dec_step_rate_rem >> 8; + if (dec_step_rate > foo) + { + dec_step_rate -= foo; + if (dec_step_rate > old_dec_step_rate) + printf("*** While decelerating, the step rate overflowed: " + "dec_step_rate = %u; dec_step_rate_rem = %u\n", + dec_step_rate, dec_step_rate_rem); + + if (dec_step_rate < block->final_rate) + dec_step_rate = block->final_rate; + } + else + dec_step_rate = block->final_rate; + dec_step_rate_rem &= 0xff; + dtimer = calc_timer(dec_step_rate, &step_loops); + deceleration_time += dtimer; + } + else + { + // Must make this call as it has side effects + step_events_completed += step_loops; + coast_time += calc_timer(acc_step_rate, &step_loops); + } + } + +#endif + + if (block->message[0] != '\0') + printf("\n%s\n", block->message); + + out_bits = block->direction_bits; + count_direction[X_AXIS] = ((out_bits & (1<steps[Z_AXIS] != 0) + { + float delta_z = block->steps[Z_AXIS] * FPTOF(axis_steps_per_unit_inverse[Z_AXIS]); + z_height += count_direction[Z_AXIS] * delta_z; + z1[iz] = block->initial_rate; + z2[iz] = acc_step_rate; + iz++; + } + + i++; + if (simulator_dump_speeds) + { + float total_time = (float)(acceleration_time + coast_time + deceleration_time /*- last_time */) / 2000000.0; + float speed_xyze = FPTOF(block->millimeters)/total_time; + float dx = (float)block->steps[X_AXIS]/axis_steps_per_unit[0]; + float dy = (float)block->steps[Y_AXIS]/axis_steps_per_unit[1]; + float dz = (float)block->steps[Z_AXIS]/axis_steps_per_unit[2]; + float speed_xyz = sqrt(dx*dx+dy*dy+dz*dz) / total_time; + + printf("%d %s: z=%4.1f entry=%5d, peak=%5d, final=%5d steps/s; planned=%d; " + "feed_rate=%6.2f mm/s; xyze-dist/t=%6.2f, xyz-dist/t=%6.2f mm/s\n", + i, action, z_height, block->initial_rate, acc_step_rate, dec_step_rate, + block->planned, FPTOF(block->feed_rate), speed_xyze, speed_xyz); + } + else + printf("%d %s: z=%4.1f entry=%5d, peak=%5d, final=%5d steps/s; planned=%d; " + "feed_rate=%6.2f mm/s (x/y/z/e=%d/%d/%d/%d)\n", + i, action, z_height, block->initial_rate, acc_step_rate, + dec_step_rate, block->planned, FPTOF(block->feed_rate), + count_direction[X_AXIS]*block->steps[X_AXIS], count_direction[Y_AXIS]*block->steps[Y_AXIS], + count_direction[Z_AXIS]*block->steps[Z_AXIS], count_direction[E_AXIS]*block->steps[E_AXIS]); + + planner_counts[max(0, min(block->planned, BLOCK_BUFFER_SIZE))] += 1; + total_time += (float)(acceleration_time + coast_time + deceleration_time) / 2000000.0; + + if (block->message[0] != '\0') + printf("\n"); + + if (discard) + plan_discard_current_block(); +} + +void plan_dump_run_data(void) +{ + int cnt, i, ihours, imins, isecs, idsecs; + float ttime = total_time; + uint32_t ztot1, zavg_min1, zavg_max1; + uint32_t ztot2, zavg_min2, zavg_max2; + float zavg1, zavg2; + + ihours = (int)(ttime / (60.0 * 60.0)); + ttime -= (float)(ihours * 60 * 60); + imins = (int)(ttime / 60.0); + ttime -= (float)(imins * 60); + isecs = (int)ttime; + ttime -= (float)isecs; + idsecs = (int)(0.5 + ttime * 100.0); + printf("Total time print time is %02d:%02d:%02d.%02d (%f seconds)\n", + ihours, imins, isecs, idsecs, total_time); + + printf("Planner counts:\n"); + for (i = 0; i <= BLOCK_BUFFER_SIZE; i++) + if (planner_counts[i]) + printf(" %d: %d\n", i, planner_counts[i]); + + memset(planner_counts, 0, sizeof(planner_counts)); + + ztot1 = 0.0; + ztot2 = 0.0; + zavg_min1 = z1[2]; + zavg_max1 = z1[2]; + zavg_min2 = z2[2]; + zavg_max2 = z2[2]; + cnt = 0; + for (i = 2; i < (iz - 2); i++) + { + if (z1[i] < 1000) continue; + cnt++; + if (z1[i] < zavg_min1) zavg_min1 = z1[i]; + if (z1[i] > zavg_max1) zavg_max1 = z1[i]; + if (z2[i] < zavg_min2) zavg_min2 = z2[i]; + if (z2[i] > zavg_max2) zavg_max2 = z2[i]; + ztot1 += z1[i]; + ztot2 += z2[i]; + } + zavg1 = (float)ztot1 / (float)cnt; + zavg2 = (float)ztot2 / (float)cnt; + printf("Min/Max/Average entry speed = %d / %d / %f steps/s\n", + zavg_min1, zavg_max1, zavg1); + printf("Min/Max/Average peak speed = %d / %d / %f steps/s\n", + zavg_min2, zavg_max2, zavg2); +} + +#if 0 +void plan_dump_current_block(int discard) +{ + block_t *block; + int counter_x, counter_y, counter_z, counter_e, counter_enew, n, + pos, steps_x, steps_y, steps_z, steps_e, steps_enew, + step_events_completed, total_steps[5]; + int extra_e; + static int i = 0; +#define BUFSIZE 1024 + char *x, xbuf[BUFSIZE+1], *y, ybuf[BUFSIZE+1], *e, ebuf[BUFSIZE+1]; + +#if 0 + printf("vmax calculations = %d\n" + " add/subtracts = %d\n" + " multiplies = %d\n" + " divides = %d\n" + " square roots = %d\n" + "recalculations = %d\n", + record_calc, record_add, record_mul, record_div, record_sqrt, + record_recalc); +#endif + + block = plan_get_current_block(); + if (!block) + return; + + if ((block->step_event_count + 3) > BUFSIZE) + { + if (NULL == (x = (char *)malloc(block->step_event_count+3+1))) goto vm_error; + if (NULL == (y = (char *)malloc(block->step_event_count+3+1))) goto vm_error; + if (NULL == (e = (char *)malloc(block->step_event_count+3+1))) goto vm_error; + } + else + { + x = xbuf; + y = ybuf; + e = ebuf; + } + + memset(x, ' ', block->step_event_count + 3); + memset(y, ' ', block->step_event_count + 3); + memset(e, ' ', block->step_event_count + 3); + + total_steps[0] = 0; + total_steps[1] = 0; + total_steps[2] = 0; + total_steps[3] = 0; + total_steps[4] = 0; + + pos = 0; + step_events_completed = 0; + + extra_e = 0; + if (block->steps[E_AXIS] != 0 && block->steps[E_AXIS] != block->step_event_count) + { + int scale = (int)(40.0 * (1.0 - block->max_entry_speed / block->feed_rate)); + if (scale > 2) + extra_e = (int)(((block->step_event_count >> 1) * scale)/10); + } + + + counter_x = -(block->step_event_count >> 1); + counter_y = counter_x; + counter_z = counter_x; + counter_e = counter_x; + counter_enew = counter_x - extra_e; + + steps_x = 0; + steps_y = 0; + steps_z = 0; + steps_e = 0; + steps_enew = 0; + + for(;; pos++) + { + + counter_x += block->steps[X_AXIS]; + if (counter_x > 0) + { + steps_x++; + total_steps[0]++; + x[pos] = 'x'; + counter_x -= block->step_event_count; + } + + counter_y += block->steps[Y_AXIS]; + if (counter_y > 0) + { + steps_y++; + total_steps[1]++; + y[pos] = 'y'; + counter_y -= block->step_event_count; + } + + counter_z += block->steps[Z_AXIS]; + if (counter_z > 0) + { + steps_z++; + total_steps[2]++; + counter_z -= block->step_event_count; + } + + counter_e += block->steps[E_AXIS]; + if (counter_e > 0) + { + steps_e++; + total_steps[3]++; + e[pos] = 'e'; + counter_e -= block->step_event_count; + } + + counter_enew += block->steps[E_AXIS]; + if (step_events_completed == block->accelerate_until) + counter_enew += extra_e; + else if (step_events_completed == block->decelerate_after) + counter_enew += extra_e; + if (counter_enew > 0) + { + steps_enew++; + total_steps[4]++; + e[pos] = (e[pos] == ' ') ? 'E' : '*'; + counter_enew -= block->step_event_count; + } + + if (step_events_completed == block->accelerate_until) + { + x[++pos] = '|'; + y[pos] = '|'; + e[pos] = '|'; + } + step_events_completed += 1; + if (step_events_completed == block->decelerate_after) + { + x[++pos] = '|'; + y[pos] = '|'; + e[pos] = '|'; + } + if (step_events_completed >= block->step_event_count) + break; + } + + for (char *p = x + pos; (*p == ' ' || *p == '|') && p != x; ) + *p-- = '\0'; + for (char *p = y + pos; (*p == ' ' || *p == '|') && p != y; ) + *p-- = '\0'; + for (char *p = e + pos; (*p == ' ' || *p == '|') && p != e; ) + *p-- = '\0'; + + x[++pos] = '\0'; + y[pos] = '\0'; + e[pos] = '\0'; + + i++; + + if (steps_e != steps_enew || true) + printf("\n%d: %d %d %d %d %d %5.2f\n" + "%s\n%s\n%s\n", + i, steps_x, steps_y, steps_z, steps_e, steps_enew, + block->max_entry_speed/FPTOF(block->feed_rate), x, y, e); + +next: + if (discard) + plan_discard_current_block(); + + goto done; + +vm_error: + fprintf(stderr, "Insufficient virtual memory\n"); + +done: + if (x != NULL && x != xbuf) free(x); + if (y != NULL && y != ybuf) free(y); + if (e != NULL && e != ebuf) free(e); + + return; +} +#endif diff --git a/firmware/simulator/StepperAccelPlannerExtras.hh b/firmware/simulator/StepperAccelPlannerExtras.hh new file mode 100644 index 0000000..b046021 --- /dev/null +++ b/firmware/simulator/StepperAccelPlannerExtras.hh @@ -0,0 +1,36 @@ +// Declarations for various routines needed by StepperAccelPlanner.cc +// which we would prefer it not to get from elsewhere + +#ifndef STEPPERACCELPLANNEREXTRAS_HH_ + +#define _STEPPERACCELPLANNEREXTRAS_HH_ + +#include "Point.hh" +#include "SimulatorRecord.hh" +#include "StepperAccelPlanner.hh" +#include "StepperAccel.hh" + +// Debugging switch & bit flags +extern uint32_t simulator_debug; +#define DEBUG_FEEDRATE 0x01 + +extern bool simulator_dump_speeds; +extern bool simulator_use_max_feed_rate; +extern bool simulator_show_alt_feed_rate; +extern FPTYPE simulator_max_feed_rate; + +extern void plan_init_eeprom(void); +extern void st_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e); +extern void st_set_e_position(const int32_t &e); +extern int32_t st_get_position(uint8_t axis); +extern void st_wake_up(void); +extern FPTYPE calcFeedRate(const Point& from, const Point& to, int32_t interval, bool includeEAxis); +extern void setTargetNew(int32_t x, int32_t y, int32_t z, int32_t a, int32_t b, int32_t us, int8_t relative); +extern void setTarget(int32_t x, int32_t y, int32_t z, int32_t a, int32_t b, int32_t dda_interval); +extern void setSegmentAccelState(bool state); +extern void definePosition(int32_t x, int32_t y, int32_t z, int32_t a, int32_t b); +extern void plan_dump(int chart); +extern void plan_dump_current_block(int discard); +extern void plan_dump_run_data(void); + +#endif diff --git a/firmware/simulator/planner.cc b/firmware/simulator/planner.cc new file mode 100644 index 0000000..db8fbb6 --- /dev/null +++ b/firmware/simulator/planner.cc @@ -0,0 +1,172 @@ +#include +#include +#include + +#include "Simulator.hh" +#include "StepperAccelPlannerExtras.hh" +#include "StepperAccel.hh" +#include "s3g.h" + +static void usage(FILE *f, const char *prog) +{ + if (f == NULL) + f = stderr; + + fprintf(f, +"Usage: %s [? | -h] [-s] [-d mask] [-r rate] [-u] [file]\n" +" file -- The name of the .s3g file to dump. If not supplied then stdin is dumped\n" +" -d mask -- Selectively enable debugging with a bit mask \"mask\"\n" +" -r rate -- Flag feed rates which exceed \"rate\"\n" +" -s -- Display block initial, peak and final speeds (mm/s) along with rates\n" +" -u -- Display significant differences between interval based and us based feed rates\n" +" ?, -h -- This help message\n", + prog ? prog : "s3gdump"); +} + +int main(int argc, const char *argv[]) +{ + char c; + s3g_command_t cmd; + s3g_context_t *ctx; + + plan_init_eeprom(); + + simulator_use_max_feed_rate = false; + simulator_dump_speeds = false; + simulator_show_alt_feed_rate = false; + + while ((c = getopt(argc, (char **)argv, ":hd:r:su?")) != -1) + { + switch(c) + { + // Unknown switch + case ':' : + default : + usage(stderr, argv[0]); + return(1); + + // Explicit help request + case 'h' : + case '?' : + usage(stdout, argv[0]); + return(1); + + // Debug + case 'd' : + { + char *ptr = NULL; + simulator_debug = (uint32_t)(0xffffffff & strtoul(optarg, &ptr, 0)); + if (ptr == NULL || ptr == optarg) + { + fprintf(stderr, "%s: unable to parse the debug mask, \"%s\", as an integer\n", + argv[0], optarg); + return(1); + } + } + break; + + // Max feed rate + case 'r' : + { + char *ptr = NULL; + float rate; + + rate = strtof(optarg, &ptr); + if (ptr == NULL || ptr == optarg) + { + fprintf(stderr, "%s: unable to parse the feed rate, \"%s\", as a floating point number\n", + argv[0], optarg); + return(1); + } + simulator_use_max_feed_rate = true; + simulator_max_feed_rate = FTOFP(rate); + } + break; + + // Display speeds as well as rates + case 's' : + simulator_dump_speeds = true; + break; + + // Display significant differences between interval based and us based feed rates + case 'u' : + simulator_show_alt_feed_rate = true; + break; + } + } + + argc -= optind; + argv += optind; + if (argc == 0) + // Open stdin + ctx = s3g_open(0, NULL); + else + // Open the specified file + ctx = s3g_open(0, (void *)argv[0]); + + if (!ctx) + // Assume that s3g_open() has complained + return(1); + + while (!s3g_command_read(ctx, &cmd)) + { + if (cmd.cmd_id == HOST_CMD_QUEUE_POINT_NEW) + { + setTargetNew(cmd.t.queue_point_new.x, cmd.t.queue_point_new.y, cmd.t.queue_point_new.z, + cmd.t.queue_point_new.a, cmd.t.queue_point_new.b, + cmd.t.queue_point_new.us, cmd.t.queue_point_new.rel); + if (movesplanned() >= (BLOCK_BUFFER_SIZE >> 1)) plan_dump_current_block(1); + } + else if (cmd.cmd_id == HOST_CMD_QUEUE_POINT_EXT) + { + setTarget(cmd.t.queue_point_ext.x, cmd.t.queue_point_ext.y, cmd.t.queue_point_ext.z, + cmd.t.queue_point_ext.a, cmd.t.queue_point_ext.b, + cmd.t.queue_point_ext.dda); + if (movesplanned() >= (BLOCK_BUFFER_SIZE >> 1)) plan_dump_current_block(1); + } + else if (cmd.cmd_id == HOST_CMD_QUEUE_POINT_ABS) + { + setTarget(cmd.t.queue_point_abs.x, cmd.t.queue_point_abs.y, cmd.t.queue_point_abs.z, 0, 0, + cmd.t.queue_point_abs.dda); + if (movesplanned() >= (BLOCK_BUFFER_SIZE >> 1)) plan_dump_current_block(1); + } + else if (cmd.cmd_id == HOST_CMD_SET_POSITION_EXT) + { + definePosition(cmd.t.set_position_ext.x, cmd.t.set_position_ext.y, cmd.t.set_position_ext.z, + cmd.t.set_position_ext.a, cmd.t.set_position_ext.b); + } + else if (cmd.cmd_id == HOST_CMD_TOOL_COMMAND && + cmd.t.tool.subcmd_id == SLAVE_CMD_TOGGLE_VALVE) + { + bool accel = cmd.t.tool.subcmd_value != 0; + printf("*** Turn segment acceleration %s ***\n", accel ? "on" : "off"); + setSegmentAccelState(accel); + } + else + { + // Dump queued blocks? + if (cmd.cmd_id != HOST_CMD_TOOL_COMMAND || + (cmd.t.tool.subcmd_id != SLAVE_CMD_TOGGLE_MOTOR_1 && + cmd.t.tool.subcmd_id != SLAVE_CMD_TOGGLE_VALVE)) + while (movesplanned() != 0) + plan_dump_current_block(1); + + if (cmd.cmd_id != HOST_CMD_TOOL_COMMAND) + printf("*** %s ***\n", cmd.cmd_name); + else + printf("*** %s (%s (%d) for tool %u to %u) ***\n", + cmd.cmd_name, cmd.t.tool.subcmd_name, cmd.t.tool.subcmd_id, + cmd.t.tool.index, cmd.t.tool.subcmd_value); + } + } + + // Dump any remaining blocks + while (movesplanned() != 0) + plan_dump_current_block(1); + + s3g_close(ctx); + + plan_dump_run_data(); + + return(0); +} diff --git a/firmware/simulator/s3g.c b/firmware/simulator/s3g.c new file mode 100644 index 0000000..24f852d --- /dev/null +++ b/firmware/simulator/s3g.c @@ -0,0 +1,435 @@ +#include +#include +#include +#include + +#include "Simulator.hh" +#include "Commands.hh" +#include "s3g_private.h" +#include "s3g_stdio.h" +#include "s3g.h" + +typedef struct { + union { + int32_t i; + uint32_t u; + unsigned char c[4]; + } u; +} foo_32_t; + +typedef struct { + uint8_t cmd_id; + size_t cmd_len; + const char *cmd_name; +} s3g_command_info_t; + +static const s3g_command_info_t command_table_raw[] = { + /* 0 */ {HOST_CMD_VERSION, 0, "version"}, + /* 1 */ {HOST_CMD_INIT, 0, "initialize"}, + /* 2 */ {HOST_CMD_GET_BUFFER_SIZE, 0, "get buffer size"}, + /* 3 */ {HOST_CMD_CLEAR_BUFFER, 0, "clear buffer"}, + /* 4 */ {HOST_CMD_GET_POSITION, 0, "get position"}, + /* 5 */ {HOST_CMD_GET_RANGE, 0, "get range"}, + /* 6 */ {HOST_CMD_SET_RANGE, 0, "set range"}, + /* 7 */ {HOST_CMD_ABORT, 0, "abort"}, + /* 8 */ {HOST_CMD_PAUSE, 0, "pause"}, + /* 9 */ {HOST_CMD_PROBE, 0, "probe"}, + /* 10 */ {HOST_CMD_TOOL_QUERY, 0, "tool query"}, + /* 11 */ {HOST_CMD_IS_FINISHED, 0, "is finished?"}, + /* 12 */ {HOST_CMD_READ_EEPROM, 0, "read EEPROM"}, + /* 13 */ {HOST_CMD_WRITE_EEPROM, 0, "write EEPROM"}, + /* 14 */ {HOST_CMD_CAPTURE_TO_FILE, 0, "capture to file"}, + /* 15 */ {HOST_CMD_END_CAPTURE, 0, "end capture"}, + /* 16 */ {HOST_CMD_PLAYBACK_CAPTURE, 0, "playback capture"}, + /* 17 */ {HOST_CMD_RESET, 0, "sortware reset"}, + /* 18 */ {HOST_CMD_NEXT_FILENAME, 0, "next SD card filename"}, + /* 19 */ {HOST_CMD_GET_DBG_REG, 0, "get debug register"}, + /* 20 */ {HOST_CMD_GET_BUILD_NAME, 0, "get build name"}, + /* 21 */ {HOST_CMD_GET_POSITION_EXT, 0, "get position extended"}, + /* 22 */ {HOST_CMD_EXTENDED_STOP, 0, "extended stop"}, + /* 25 */ {HOST_CMD_GET_COMMUNICATION_STATS, 0, "get communication stats"}, + /* 112 */ {HOST_CMD_DEBUG_ECHO, 0, "debug echo"}, + /* 129 */ {HOST_CMD_QUEUE_POINT_ABS, 16, "queue point absolute"}, + /* 130 */ {HOST_CMD_SET_POSITION, 12, "set position"}, + /* 131 */ {HOST_CMD_FIND_AXES_MINIMUM, 7, "find axes minimum"}, + /* 132 */ {HOST_CMD_FIND_AXES_MAXIMUM, 7, "find axes maximum"}, + /* 133 */ {HOST_CMD_DELAY, 4, "delay"}, + /* 134 */ {HOST_CMD_CHANGE_TOOL, 1, "change tool"}, + /* 135 */ {HOST_CMD_WAIT_FOR_TOOL, 5, "wait for tool ready"}, + /* 136 */ {HOST_CMD_TOOL_COMMAND, 0xffffffff, "tool action"}, + /* 137 */ {HOST_CMD_ENABLE_AXES, 1, "enable/disable axes"}, + /* 138 */ {138, 2, "user block"}, + /* 139 */ {HOST_CMD_QUEUE_POINT_EXT, 24, "queue point extended"}, + /* 140 */ {HOST_CMD_SET_POSITION_EXT, 20, "set position extended"}, + /* 141 */ {HOST_CMD_WAIT_FOR_PLATFORM, 5, "wait for platform ready"}, + /* 142 */ {HOST_CMD_QUEUE_POINT_NEW, 25, "queue new point"}, + /* 143 */ {HOST_CMD_STORE_HOME_POSITION, 1, "store home position"}, + /* 144 */ {HOST_CMD_RECALL_HOME_POSITION, 1, "recall home position"}, + /* 201 */ {HOST_CMD_SET_MAX_ACCEL , 0, "set maximum accelerations"}, + /* 203 */ {HOST_CMD_SET_MAX_FEEDRATE, 0, "set maximum feed rates"}, + /* 204 */ {HOST_CMD_SET_DEFAULT_ACCEL, 0, "set default accelerations"}, + /* 205 */ {HOST_CMD_SET_ADVANCED_ACCEL, 0, "set advanced accelerations"}, + /* 206 */ {HOST_CMD_SET_ADVANCED_ACCEL2, 0, "set filament diameter"}, + /* 207 */ {HOST_CMD_SET_ADVANCE_K , 0, "set 'advance' K constant"}, + /* 208 */ {HOST_CMD_SET_EXTRUDER_STEPSMM, 0, "set extruder steps per mm"}, + /* 209 */ {HOST_CMD_SET_ACCELERATION, 0, "enable/disable acceleration"}, + /* 210 */ {HOST_CMD_MOOD_LIGHT_SET_RGB, 0, "set mood light RGB"}, + /* 211 */ {HOST_CMD_MOOD_LIGHT_SET_HSB, 0, "set mood light HSB"}, + /* 212 */ {HOST_CMD_MOOD_LIGHT_PLAY_SCRIPT, 0, "play mood light script"}, + /* 213 */ {HOST_CMD_BUZZER_REPEATS, 0, "set buzzer repeats"}, + /* 214 */ {HOST_CMD_BUZZER_BUZZ, 0, "enable/disable buzzer"}, + /* 215 */ {HOST_CMD_SET_AXIS_STEPS_MM, 0, "set axis steps per mm"} +}; + +static s3g_command_info_t command_table[256]; + +// Not thread safe + +static int table_initialized = 0; + +static int s3g_init(void) +{ + int i, istat; + const s3g_command_info_t *p; + + if (table_initialized != 0) + return(0); + + // Initialize the indexed command table + memset(command_table, 0, sizeof(command_table)); + + istat = 0; + p = command_table_raw; + + // Load the indexed command table, looking for conflicts + for (i = 0; i < sizeof(command_table_raw) / sizeof(s3g_command_info_t); i++, p++) + { + if ((command_table[p->cmd_id].cmd_len != 0 || + command_table[p->cmd_id].cmd_name != NULL)) + { + // Table already has an entry for this command id + // Make sure that the lengths don't conflict + if (command_table[p->cmd_id].cmd_len != p->cmd_len) + { + fprintf(stderr, + "s3g_init(%d): Two commands with identical ids (%d) but " + "different lengths encountered; ignoring \"%s\"\n", + __LINE__, p->cmd_id, p->cmd_name ? p->cmd_name : ""); + istat = -1; + continue; + } + } + command_table[p->cmd_id].cmd_id = p->cmd_id; + command_table[p->cmd_id].cmd_len = p->cmd_len; + command_table[p->cmd_id].cmd_name = p->cmd_name; + } + + table_initialized = -1; + + return(istat); +} + + +s3g_context_t *s3g_open(int type, void *src) +{ + s3g_context_t *ctx; + + (void)type; // Only type is a file + + ctx = (s3g_context_t *)calloc(1, sizeof(s3g_context_t)); + if (!ctx) + { + fprintf(stderr, "s3g_open(%d): Unable to allocate VM; %s (%d)\n", + __LINE__, strerror(errno), errno); + return(NULL); + } + + if (s3g_stdio_open(ctx, src)) + return(NULL); + + return(ctx); +} + + +int s3g_close(s3g_context_t *ctx) +{ + int iret; + + if (!ctx) + return(0); + + iret = (ctx->close != NULL) ? (*ctx->close)(ctx->rw_ctx) : 0; + + free(ctx); + + return(iret); +} + + +int s3g_command_read_ext(s3g_context_t *ctx, s3g_command_t *cmd, + unsigned char *buf, size_t maxbuf, size_t *buflen) +{ + unsigned char *buf0 = buf; + ssize_t bytes_expected, bytes_read; + s3g_command_info_t *ct; + s3g_command_t dummy; + foo_32_t f32; + int iret; + uint8_t ui8arg; + + iret = -1; + + if (buflen) + *buflen = 0; + + // We have to have a read context + // We don't need a command context to return the command in + if (!ctx || !buf || maxbuf == 0) + { + fprintf(stderr, "s3g_command_get(%d): Invalid call; ctx=%p, buf=%p, " + "maxbuf=%u\n", __LINE__, (void *)ctx, (void *)buf, maxbuf); + errno = EINVAL; + return(-1); + } + else if (!ctx->read) + { + fprintf(stderr, "s3g_command_get(%d): Invalid context; " + "ctx->read=NULL\n", __LINE__); + errno = EINVAL; + return(-1); + } + else if (!buf || maxbuf == 0) + { + fprintf(stderr, "s3g_command_get(%d): Invalid context; " + "ctx->read=NULL\n", __LINE__); + errno = EINVAL; + return(-1); + } + + // Initialize command table + s3g_init(); + + if (1 != (bytes_expected = (*ctx->read)(ctx->rw_ctx, buf, maxbuf, 1))) + { + // End of file condition? + if (bytes_expected == 0) + return(1); // EOF + + fprintf(stderr, + "s3g_command_get(%d): Error while reading from the s3g file; " + "%s (%d)\n", + __LINE__, strerror(errno), errno); + return(-1); + } + + ct = command_table + buf[0]; // &command_table[buf[0]] + + buf += 1; + maxbuf -= 1; + + if (!cmd) + cmd = &dummy; + + cmd->cmd_id = ct->cmd_id; + cmd->cmd_name = ct->cmd_name; + cmd->cmd_len = ct->cmd_len; + + if (ct->cmd_name == NULL) + { + fprintf(stderr, + "s3g_command_get(%d): Unrecognized command, %d\n", + __LINE__, buf[0]); + goto done; + } + + switch(cmd->cmd_id) + { + default : + case HOST_CMD_CHANGE_TOOL : + case HOST_CMD_ENABLE_AXES : + case HOST_CMD_SET_POSITION : + case HOST_CMD_DELAY : + case HOST_CMD_FIND_AXES_MINIMUM : + case HOST_CMD_FIND_AXES_MAXIMUM : + case HOST_CMD_WAIT_FOR_TOOL : + case HOST_CMD_WAIT_FOR_PLATFORM : + case HOST_CMD_STORE_HOME_POSITION : + case HOST_CMD_RECALL_HOME_POSITION : + case HOST_CMD_SET_MAX_ACCEL : + case HOST_CMD_SET_MAX_FEEDRATE : + case HOST_CMD_SET_DEFAULT_ACCEL : + case HOST_CMD_SET_ADVANCED_ACCEL : + case HOST_CMD_SET_ADVANCED_ACCEL2 : + case HOST_CMD_SET_ADVANCE_K : + case HOST_CMD_SET_EXTRUDER_STEPSMM : + case HOST_CMD_SET_ACCELERATION : + case HOST_CMD_MOOD_LIGHT_SET_RGB : + case HOST_CMD_MOOD_LIGHT_SET_HSB : + case HOST_CMD_MOOD_LIGHT_PLAY_SCRIPT : + case HOST_CMD_BUZZER_REPEATS : + case HOST_CMD_BUZZER_BUZZ : + case HOST_CMD_SET_AXIS_STEPS_MM : + // Just read the data + bytes_expected = (ssize_t)(ct->cmd_len & 0x7fffffff); + if ((bytes_read = (*ctx->read)(ctx->rw_ctx, buf, maxbuf, + ct->cmd_len)) != bytes_expected) + goto io_error; + + buf += bytes_read; + maxbuf -= bytes_read; + break; + +#define GET_INT32(v) \ + if (maxbuf < 4) goto trunc; \ + if (4 != (bytes_read = (*ctx->read)(ctx->rw_ctx, buf, maxbuf, 4))) \ + goto io_error; \ + memcpy(&f32.u.c, buf, 4); \ + buf += bytes_read; \ + maxbuf -= bytes_read; \ + cmd->t.v = f32.u.i + +#define GET_UINT8(v) \ + if (maxbuf < 1) goto trunc; \ + if (1 != (bytes_read = (*ctx->read)(ctx->rw_ctx, buf, maxbuf, 1))) \ + goto io_error; \ + ui8arg = buf[0]; \ + buf += bytes_read; \ + maxbuf -= bytes_read; \ + cmd->t.v = ui8arg + +#define ZERO(v,c) cmd->t.v = (c)0 + + case HOST_CMD_TOOL_COMMAND : + // This command is VERY MBI specific + if ((ssize_t)3 != (*ctx->read)(ctx->rw_ctx, buf, maxbuf, 3)) + goto io_error; + if (cmd) + cmd->cmd_len = (size_t)buf[2]; + cmd->t.tool.subcmd_id = buf[1]; + cmd->t.tool.index = buf[0]; + cmd->t.tool.subcmd_len = bytes_expected = (ssize_t)buf[2]; + if ((bytes_read = (*ctx->read)(ctx->rw_ctx, buf + 3, maxbuf - 3, + (size_t)buf[2])) != bytes_expected) + goto io_error; + + if (cmd->t.tool.subcmd_len == 1) + cmd->t.tool.subcmd_value = (uint16_t)buf[3]; + else if (cmd->t.tool.subcmd_len > 1) + memcpy((void *)&cmd->t.tool.subcmd_value, buf + 3, sizeof(uint16_t)); + else + cmd->t.tool.subcmd_value = 0; + + maxbuf -= 3 + bytes_read; + buf += 3 + bytes_read; + + switch (cmd->t.tool.subcmd_id) + { + case SLAVE_CMD_SET_TEMP : + cmd->t.tool.subcmd_name = "set temperature"; + break; + + case SLAVE_CMD_SET_PLATFORM_TEMP : + cmd->t.tool.subcmd_name = "set platform temperature"; + break; + + case SLAVE_CMD_SET_MOTOR_1_PWM : + cmd->t.tool.subcmd_name = "set motor 1 PWM"; + break; + + case SLAVE_CMD_TOGGLE_MOTOR_1 : + cmd->t.tool.subcmd_name = "toggle motor 1"; + break; + + case SLAVE_CMD_TOGGLE_ABP : + cmd->t.tool.subcmd_name = "toggle ABP"; + break; + + case SLAVE_CMD_TOGGLE_VALVE : + if (cmd->t.tool.subcmd_value == 0) + cmd->t.tool.subcmd_name = "segment acceleration off"; + else + cmd->t.tool.subcmd_name = "segment acceleration on"; + break; + + default : + cmd->t.tool.subcmd_name = "unknown subcommand"; + break; + } + break; + + case HOST_CMD_SET_POSITION_EXT : + // x4, y4, z4, a4, b4 = 20 bytes + GET_INT32(set_position_ext.x); + GET_INT32(set_position_ext.y); + GET_INT32(set_position_ext.z); + GET_INT32(set_position_ext.a); + GET_INT32(set_position_ext.b); + break; + + case HOST_CMD_QUEUE_POINT_EXT : + // x4, y4, z4, a4, b4, dda4 = 24 bytes + GET_INT32(queue_point_ext.x); + GET_INT32(queue_point_ext.y); + GET_INT32(queue_point_ext.z); + GET_INT32(queue_point_ext.a); + GET_INT32(queue_point_ext.b); + GET_INT32(queue_point_ext.dda); + ZERO(queue_point_ext.dummy_rel, uint8_t); + break; + + case HOST_CMD_QUEUE_POINT_NEW : + // x4, y4, z4, a4, b4, us4, relative = 25 bytes + GET_INT32(queue_point_new.x); + GET_INT32(queue_point_new.y); + GET_INT32(queue_point_new.z); + GET_INT32(queue_point_new.a); + GET_INT32(queue_point_new.b); + GET_INT32(queue_point_new.us); + GET_UINT8(queue_point_new.rel); + break; + + case HOST_CMD_QUEUE_POINT_ABS : + // x4, y4, z4, dda4 = 16 bytes + GET_INT32(queue_point_abs.x); + GET_INT32(queue_point_abs.y); + GET_INT32(queue_point_abs.z); + GET_INT32(queue_point_abs.dda); + ZERO(queue_point_abs.dummy_a, int32_t); + ZERO(queue_point_abs.dummy_b, int32_t); + ZERO(queue_point_abs.dummy_rel, uint8_t); + break; + } + +#undef ZERO +#undef GET_UINT8 +#undef GET_INT32 + + iret = 0; + goto done; + +io_error: + fprintf(stderr, + "s3g_command_get(%d): Error while reading from the s3g file; " + "%s (%d)\n", + __LINE__, strerror(errno), errno); + iret = -1; + goto done; + +trunc: + fprintf(stderr, + "s3g_command_get(%d): Caller supplied read buffer is too small", + __LINE__); + iret = -1; + +done: + if (buflen) + *buflen = (size_t)(buf - buf0); + + return(iret); +} + +int s3g_command_read(s3g_context_t *ctx, s3g_command_t *cmd) +{ + unsigned char buf[1024]; + return(s3g_command_read_ext(ctx, cmd, buf, sizeof(buf), NULL)); +} diff --git a/firmware/simulator/s3g.h b/firmware/simulator/s3g.h new file mode 100644 index 0000000..92b6cf2 --- /dev/null +++ b/firmware/simulator/s3g.h @@ -0,0 +1,312 @@ +// s3g.h +// Structure and routine definitions for the s3g library + +#ifndef S3G_H_ + +#define S3G_H_ + +#include +#include "Commands.hh" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef S3G_CONTEXT_T_ +#define S3G_CONTEXT_T_ +typedef void s3g_context_t; +#endif + +// s3g_queue_point_abs, s3g_queue_point_ext, and s3g_queue_point_new +// all have the same size, packing and layout. They can be cast to +// one another and the relevant fields extracted. The dummy_ fields +// will be set to zero when appropriate. +typedef struct { + int32_t x; + int32_t y; + int32_t z; + int32_t dummy_a; + int32_t dummy_b; + int32_t dda; + uint8_t dummy_rel; +} s3g_queue_point_abs; + +typedef struct { + int32_t x; + int32_t y; + int32_t z; + int32_t a; + int32_t b; + int32_t dda; + uint8_t dummy_rel; +} s3g_queue_point_ext; + +typedef struct { + int32_t x; + int32_t y; + int32_t z; + int32_t a; + int32_t b; + int32_t us; + uint8_t rel; +} s3g_queue_point_new; + +typedef struct { + uint8_t index; +} s3g_change_tool; + +typedef struct { + uint8_t axes; +} s3g_enable_axes; + +typedef struct { + int32_t x; + int32_t y; + int32_t z; +} s3g_set_position; + +typedef struct { + int32_t x; + int32_t y; + int32_t z; + int32_t a; + int32_t b; +} s3g_set_position_ext; + +typedef struct { + uint32_t millis; +} s3g_delay; + +typedef struct { + uint8_t flags; + uint32_t feed_rate; // microseconds per step + uint16_t timeout; // seconds +} s3g_find_axes_minimum; + +typedef struct { + uint8_t flags; + uint32_t feed_rate; // microseconds per step + uint16_t timeout; // seconds +} s3g_find_axes_maximum; + +typedef struct { + uint8_t index; + uint16_t ping_delay; + uint16_t timeout; +} s3g_wait_for_tool; + +typedef struct { + uint8_t index; + uint16_t ping_delay; + uint16_t timeout; +} s3g_wait_for_platform; + +typedef struct { + uint8_t axes; +} s3g_store_home_position; + +typedef struct { + uint8_t axes; +} s3g_recall_home_position; + +typedef struct { + uint8_t index; + uint8_t subcmd_id; + size_t subcmd_len; + const char *subcmd_name; + uint16_t subcmd_value; +} s3g_tool; + +typedef struct { + int32_t x; + int32_t y; + int32_t z; + int32_t a; +} s3g_set_max_accel; + +typedef struct { + int32_t x; + int32_t y; + int32_t z; + int32_t a; +} s3g_set_max_feedrate; + +typedef struct { + int32_t s; + int32_t t; +} s3g_set_default_accel; + +typedef struct { + int32_t s; + int32_t t; + int32_t x; + int32_t z; +} s3g_set_advanced_accel; + +typedef struct { + int32_t d; +} s3g_set_filament_diameter; + +typedef struct { + int32_t s; + int32_t k; +} s3g_set_advance_k; + +typedef struct { + int32_t a; +} s3g_set_extruder_stepsmm; + +typedef struct { + uint8_t s; +} s3g_set_acceleration; + +typedef struct { + int32_t r; + int32_t g; + int32_t b; + int32_t fade_speed; + int32_t write_to_eeprom; +} s3g_mood_set_rgb; + +typedef struct { + int32_t h; + int32_t s; + int32_t b; + int32_t write_to_eeprom; +} s3g_mood_set_hsb; + +typedef struct { + int32_t script_id; + int32_t write_to_eeprom; +} s3g_mood_play_script; + +typedef struct { + uint8_t repeats; +} s3g_buzzer_repeats; + +typedef struct { + uint8_t buzzes; + uint8_t duration; + uint8_t repeats; +} s3g_buzzer_buzz; + +typedef struct { + int64_t x; // int32_t * 10000 + int64_t y; // int32_t * 10000 + int64_t z; // int32_t * 10000 + int64_t a; // int32_t * 10000 +} s3g_set_axis_steps_mm; + +// s3g_command_t +// An individual command read from a .s3g file is stored in +// this data structure. You need to know from the command id +// which member of the union to look at. + +// #define constants from the command ids are available in Commands.hh + +typedef struct { + uint8_t cmd_id; + size_t cmd_len; + const char *cmd_name; + union { + s3g_queue_point_abs queue_point_abs; + s3g_queue_point_ext queue_point_ext; + s3g_queue_point_new queue_point_new; + s3g_change_tool change_tool; + s3g_enable_axes enable_axes; + s3g_set_position set_position; + s3g_set_position_ext set_position_ext; + s3g_delay delay; + s3g_find_axes_minimum find_axes_minimum; + s3g_find_axes_maximum find_axes_maximum; + s3g_wait_for_tool wait_for_tool; + s3g_wait_for_platform wait_for_platform; + s3g_store_home_position store_home_position; + s3g_recall_home_position recall_home_position; + s3g_tool tool; + s3g_set_max_accel set_max_accel; + s3g_set_max_feedrate set_max_feedrate; + s3g_set_default_accel set_default_accel; + s3g_set_advanced_accel set_advanced_accel; + s3g_set_filament_diameter set_filament_diameter; + s3g_set_advance_k set_advance_k; + s3g_set_extruder_stepsmm set_extruder_stepsmm; + s3g_set_acceleration set_acceleration; + s3g_mood_set_rgb mood_set_rgb; + s3g_mood_set_hsb mood_set_hsb; + s3g_mood_play_script mood_play_script; + s3g_buzzer_repeats buzzer_repeats; + s3g_buzzer_buzz buzzer_buzz; + s3g_set_axis_steps_mm set_axis_steps_mm; + } t; +} s3g_command_t; + +#define S3G_INPUT_TYPE_FILE 0 // stdin or a named disk file + +// Obtain an s3g_context for an input source of type S3G_INPUT_TYPE_. +// The context returned must be disposed of by calling s3g_close(). +// +// Call arguments: +// +// int type +// Input source type. Must be one of +// +// S3G_INPUT_TYPE_FILE +// +// void *src +// Input source information for the selected input type +// +// S3G_INPUT_TYPE_FILE -- const char *filename or NULL for stdin +// +// Return values: +// +// != NULL -- Success +// == NULL -- Error; consult errno + +s3g_context_t *s3g_open(int type, void *src); + + +// Read a single command from the s3g context, storing the result in the +// supplied s3g_command_t structure +// +// Call arguments: +// +// s3g_context_t *ctx +// Context obtained by calling s3g_open(). +// +// s3g_command_t *cmd +// Pointer to a s3g_command_t structure to store the read command in. If NULL +// is passed for this argument, then the command is read but no information about +// the command is returned. +// +// Return values: +// +// 0 -- Success +// 1 -- Unrecognized command encountered; not possible to read further +// -1 -- Read error; check errno + +int s3g_command_read(s3g_context_t *ctx, s3g_command_t *cmd); + +int s3g_command_read_ext(s3g_context_t *ctx, s3g_command_t *cmd, + unsigned char *rawbuf, size_t maxbuf, size_t *len); + + +// Close the s3g input source, releasing any resources +// +// Call arguments: +// +// s3g_context_t *ctx +// Context obtained by calling s3g_open(). +// +// Return values: +// +// 0 -- Success +// -1 -- Close error; check errno + +int s3g_close(s3g_context_t *ctx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/simulator/s3g_private.h b/firmware/simulator/s3g_private.h new file mode 100644 index 0000000..230218a --- /dev/null +++ b/firmware/simulator/s3g_private.h @@ -0,0 +1,38 @@ +// s3g_private.h +// Private declarations for the s3g library + +#ifndef S3G_PRIVATE_H_ + +#define S3G_PRIVATE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// read, write, and close procedure definitions for .s3g file drivers + +typedef ssize_t s3g_read_proc_t(void *ctx, unsigned char *buf, size_t maxbuf, size_t nbytes); +typedef ssize_t s3g_write_proc_t(void *ctx, unsigned char *buf, size_t nbytes); +typedef int s3g_close_proc_t(void *ctx); + +// The actual s3g_context_t declaration + +#ifndef S3G_CONTEXT_T_ +#define S3G_CONTEXT_T_ +typedef struct { + s3g_read_proc_t *read; // File driver read procedure; req'd for reading + s3g_write_proc_t *write; // File driver write procedure; req'd for writing + s3g_close_proc_t *close; // File driver close procedure; optional + void *rw_ctx; // File driver private context +} s3g_context_t; +#endif + +// File driver open procedure; no need at present to keep this in the context + +typedef int s3g_open_proc_t(s3g_context_t *ctx, void *src); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/simulator/s3g_stdio.c b/firmware/simulator/s3g_stdio.c new file mode 100644 index 0000000..88b8e3a --- /dev/null +++ b/firmware/simulator/s3g_stdio.c @@ -0,0 +1,279 @@ +#include +#include +#include +#include +#include +#include + +#include "s3g_stdio.h" + +// Identify temporary read errors +// Can be os-specific + +#define FD_TEMPORARY_ERR() \ + (errno == EINTR || errno == ENOMEM || errno == ENOSR || errno == ENOBUFS) + +// This driver's private context + +typedef struct { + int fd; // File descriptor; < 0 indicates that the file is not open +} s3g_rw_stdio_ctx_t; + + +// stdio_close +// +// Close the input source and release the allocated driver context +// +// Call arguments: +// +// void *ctx +// Private driver context allocated by stdio_open(). +// +// Return values: +// +// 0 -- Success +// -1 -- Error; check errno may be a close() error which is significant when writing + +static s3g_close_proc_t stdio_close; +static int stdio_close(void *ctx) +{ + int fd; + s3g_rw_stdio_ctx_t *myctx = (s3g_rw_stdio_ctx_t *)ctx; + + // Sanity check + if (!myctx) + { + errno = EINVAL; + return(-1); + } + + fd = myctx->fd; + free(myctx); + + if (fd < 0) + { + errno = EBADF; + return(-1); + } + + return(close(fd)); +} + + +// stdio_read_retry +// +// Read the specified number of bytes into the buffer. Temporary read errors will +// be handled by this routine which will try to read all nbytes unless EOF is +// encountered. +// +// When the end of the file is reached before reading nbytes, the return value, R, will +// satisfy 0 <= R < nbytes. +// +// *** Length checking is not performed on the buffer +// *** That is presumed to be done by stdio_read() +// +// Call arguments: +// +// int fd +// Descriptor of the open file to read from. +// +// unsigned char *buf +// Pointer to a buffer to read the data to. Length of the buffer must be at +// least nbytes long. Buffer will NOT be NUL terminated. +// +// size_t nbytes +// The number of bytes to read from the input source. +// +// Return values: +// +// > 0 -- Number of bytes read. Guaranteed to be == nbytes UNLESS a permanent read +// error occurs or the end of the file is reached. +// 0 -- End of file reached (unless nbytes == 0) +// -1 -- File error; check errno + +static ssize_t stdio_read_retry(int fd, unsigned char *buf, size_t nbytes) +{ + ssize_t n, nread; + + // Save read() the bother of this test + if (fd < 0) + { + errno = EBADF; + return((ssize_t)-1); + } + + // Repeatedly call until we've read the requested amount of data or gotten + // an error which isn't temporary + nread = 0; + for (;;) + { + if ((n = read(fd, buf, nbytes)) <= 0 && FD_TEMPORARY_ERR()) + continue; + if (n == 0) + // EOF reached + return(nread); + nread += n; + nbytes -= n; + if (nbytes == 0) + return(nread); + buf += n; + } +} + + +// stdio_read +// +// Read the specified number of bytes from the input source, placing at most +// maxbuf bytes into the buffer, buf. If nbytes > maxbuf, then nbytes will be +// read. However, only the first maxbuf bytes read will be stored in buf. Note +// that buf will not be NUL terminated. +// +// Unless a permanent error occurs, stdio_read() guarantees that nbytes will actually +// be read. If less than nbytes is returned, then either an end-of-file condition +// occurred or there was a non-temporary read error. +// +// On return, stdio_read() indicates how many bytes were actually read. +// +// Call arguments: +// +// void *ctx +// Private driver context created by stdio_open(). +// +// unsigned char *buf +// Buffer into which to read the data. At most maxbuf bytes will be stored in this +// buffer. Any bytes read beyond maxbuf will be discarded. That only occurs when +// nbytes > maxbuf. If buf == NULL, then maxbuf will be considered 0 and nbytes +// will be read and discarded. +// +// size_t maxbuf +// Maximum number of bytes to store in buf. Maxbuf must not exceed the size of +// the buffer pointed at by buf. +// +// size_t nbytes +// The number of bytes to read from the input source. +// +// Return values: +// +// > 0 -- Number of bytes read. If the returned value is less than nbytes, then an +// end of file condition has occurred. +// 0 -- End of file reached or nbytes == 0 +// -1 -- Read error or invalid call arguments; check errno +// +static s3g_read_proc_t stdio_read; +static ssize_t stdio_read(void *ctx, unsigned char *buf, size_t maxbuf, size_t nbytes) +{ + s3g_rw_stdio_ctx_t *myctx = (s3g_rw_stdio_ctx_t *)ctx; + ssize_t n; + + // Sanity check + if (!myctx) + { + errno = EINVAL; + return((ssize_t)-1); + } + + // Return now if nothing to read + if (nbytes == 0) + return((ssize_t)0); + + // Treat NULL for buf as though maxbuf == 0 + if (!buf) + maxbuf = 0; + + // Buffer is big enough to contain the entire read + if (nbytes <= maxbuf) + return(stdio_read_retry(myctx->fd, buf, nbytes)); + + // Buffer is not large enough to contain the entire read + if ((n = stdio_read_retry(myctx->fd, buf, maxbuf)) <= 0) + return(n); + + // Read the remaining number of bytes requested without + // shoving them into buf (which is full) + nbytes -= n; + { + unsigned char tmpbuf[1024]; + size_t nread; + + while (nbytes != 0) + { + + nread = (sizeof(tmpbuf) < nbytes) ? (size_t)sizeof(tmpbuf) : nbytes; + if ((n = stdio_read_retry(myctx->fd, tmpbuf, nread)) <= 0) + return(n); + nbytes -= nread; + } + } + return(0); +} + + +// s3g_stdio_open +// Our public open routine. This is the only public routine for the driver. +// +// Call arguments +// +// s3g_context_t *ctx +// s3g context to associate ourselves with. +// +// void *src +// Input source information. For this driver, a value of NULL indicates that +// the input source is stdin. Otherwise, the value is treated as a "const char *" +// pointer pointing to the name of a file to open in read only mode. A ".s3g" +// will NOT be appended to the file name. The file name must be the complete +// file name (but need not be an absolute file path). +// +// Return values: +// +// 0 -- Success +// -1 -- Error; check errno + +int s3g_stdio_open(s3g_context_t *ctx, void *src) +{ + s3g_rw_stdio_ctx_t *tmp; + + // Sanity check + if (!ctx) + { + fprintf(stderr, "s3g_stdio_open(%d): Invalid call; ctx=NULL\n", __LINE__); + errno = EINVAL; + return(-1); + } + + // Allocate memory for our "driver" context + tmp = (s3g_rw_stdio_ctx_t *)calloc(1, sizeof(s3g_rw_stdio_ctx_t)); + if (tmp == NULL) + { + fprintf(stderr, "s3g_open(%d): Unable to allocate VM; %s (%d)\n", + __LINE__, strerror(errno), errno); + return(-1); + } + + // What sort of input source: named file or stdin? + if (src == NULL) + { + // Assume we're using stdin + tmp->fd = fileno(stdin); + } + else + { + const char *fname = (const char *)src; + int fd = open(fname, O_RDONLY); + if (fd < 0) + { + fprintf(stderr, "s3g_open(%d): Unable to open the file \"%s\"; %s (%d)\n", + __LINE__, fname, strerror(errno), errno); + free(tmp); + return(-1); + } + tmp->fd = fd; + } + + // All finished and happy + ctx->close = stdio_close; + ctx->read = stdio_read; + ctx->write = NULL; + ctx->rw_ctx = tmp; + + return(0); +} diff --git a/firmware/simulator/s3g_stdio.h b/firmware/simulator/s3g_stdio.h new file mode 100644 index 0000000..4d24d15 --- /dev/null +++ b/firmware/simulator/s3g_stdio.h @@ -0,0 +1,22 @@ +// s3g_stdio.h +// Private declarations for the standard unix i/o file driver + +#ifndef S3G_STDIO_H_ + +#define S3G_STDIO_H_ + +#include "s3g_private.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Driver's open procedure + +s3g_open_proc_t s3g_stdio_open; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/simulator/s3gdump.c b/firmware/simulator/s3gdump.c new file mode 100644 index 0000000..19a664b --- /dev/null +++ b/firmware/simulator/s3gdump.c @@ -0,0 +1,253 @@ +// Simple tool to dump a .s3g file either from disk of from stdin +// +// s3gdump filename +// +// or +// +// s3gdump < filename + +#include +#include +#include +#include +#include "s3g.h" + +static void usage(FILE *f, const char *prog) +{ + if (f == NULL) + f = stderr; + + fprintf(f, +"Usage: %s -hE [file]\n" +" file -- The .s3g file to dump. If not supplied then stdin is dumped\n" +" ?, -h -- This help message\n" +" -E -- Display distance moved and ratio of extruder steps to distance\n", + prog ? prog : "s3gdump"); +} + +// For a stream of .s3g commands, cherry pick out the movement related +// commands, displaying the distance moved in 3-space (xyz) and the +// density of extruder steps over that distance [extruder steps / distance]. + +// BUG: For the time being, the steps/mm for each axis is hard coded +static void edensity(const s3g_command_t *cmd) +{ +#define AXIS_COUNT 5 +#define X_AXIS 0 +#define Y_AXIS 1 +#define Z_AXIS 2 +#define A_AXIS 3 +#define B_AXIS 4 +#define E_AXIS (A_AXIS) + + // On the first call, display column headings + static int32_t first = 1; + + // Track the previous position + static int32_t last_position[AXIS_COUNT] = {0, 0, 0, 0, 0}; + + // Steps per mm for each axis + static float axis_steps_per_unit[AXIS_COUNT] = { + 47.069852, // x + 47.069852, // y + 200.0, // z + 50.2354788069, // a + 50.2354788069 // b + }; + + if (!cmd) + return; + + // Generate column headings + if (first) + { + printf(" distance e-density (esteps)\n"); + first = 0; + } + + // Now take an action based upon the command id + switch (cmd->cmd_id) + { + + // Non motion related command; simply print the command's name + default : + if (cmd->cmd_name != NULL) + printf("- %s -\n", cmd->cmd_name); + break; + + // Command for the extruder controller + case HOST_CMD_TOOL_COMMAND : + if (cmd->cmd_name != NULL) + { + if (cmd->t.tool.subcmd_name != NULL) + printf("- %s (%s (%d) for tool %u to %u) -\n", + cmd->cmd_name, cmd->t.tool.subcmd_name, cmd->t.tool.subcmd_id, + cmd->t.tool.index, cmd->t.tool.subcmd_value); + else + printf("- %s -\n", cmd->cmd_name); + } + break; + + // Move to a new position + case HOST_CMD_QUEUE_POINT_NEW : + case HOST_CMD_QUEUE_POINT_EXT : + case HOST_CMD_QUEUE_POINT_ABS : + { + float d, delta, e_density; + int32_t esteps, t[AXIS_COUNT], zsteps; + int i; + uint8_t rel; + + // Extract the x, y, z, a, b data from the command + + if (cmd->cmd_id == HOST_CMD_QUEUE_POINT_NEW) + { + t[X_AXIS] = cmd->t.queue_point_new.x; + t[Y_AXIS] = cmd->t.queue_point_new.y; + t[Z_AXIS] = cmd->t.queue_point_new.z; + t[A_AXIS] = cmd->t.queue_point_new.a; + t[B_AXIS] = cmd->t.queue_point_new.b; + rel = cmd->t.queue_point_new.rel; + } + else if (cmd->cmd_id == HOST_CMD_QUEUE_POINT_ABS) + { + t[X_AXIS] = cmd->t.queue_point_abs.x; + t[Y_AXIS] = cmd->t.queue_point_abs.y; + t[Z_AXIS] = cmd->t.queue_point_abs.z; + t[A_AXIS] = 0; + t[B_AXIS] = 0; + rel = 0; + } + else // cmd->cmd_id == HOST_CMD_QUEUE_POINT_EXT + { + t[X_AXIS] = cmd->t.queue_point_ext.x; + t[Y_AXIS] = cmd->t.queue_point_ext.y; + t[Z_AXIS] = cmd->t.queue_point_ext.z; + t[A_AXIS] = cmd->t.queue_point_ext.a; + t[B_AXIS] = cmd->t.queue_point_ext.b; + + // No relative coordinates for "queue point ext" + rel = 0; + } + + // Process any relative coordinates + if (rel != 0) + for (i = 0; i < AXIS_COUNT; i++) + if ((rel & (1 << i)) != 0) + t[i] += last_position[i]; + + // Warn about B_AXIS steps + if (t[B_AXIS] != 0) + printf("*** Warning: %d B axis steps taken ***\n", t[B_AXIS]); + + // Now, compute the distance moved along the first three + // axes. Those would be the x, y, and z axes. + d = 0.0; + for (i = X_AXIS; i <= Z_AXIS; i++) + { + // Steps moved is new position less the old position + // Distance moved in mm is then (steps moved) / (steps / mm) + delta = (float)(t[i] - last_position[i]) / + axis_steps_per_unit[i]; + d += delta * delta; + } + + // Distance is the square root of the sum of delta^2 + d = sqrt(d); + + // Distance moved along the z axis + zsteps = t[Z_AXIS] - last_position[Z_AXIS]; + + // Steps moved along the e (a) axis + esteps = t[A_AXIS] - last_position[A_AXIS]; + + // Compute the ratio of e-steps moved to distance moved + if (d > 1.0e-5) + e_density = (float)esteps / d; + else + e_density = 0.0; + + // Now generate a line of output + // We indicate if we moved up or down in layer height as well + // that's useful for identifying the start of a new layer + if (zsteps == 0) + printf(" %7.3f %7.2f (%6d)\n", d, e_density, esteps); + else + printf("%c %7.3f %7.2f (%6d)\n", + (zsteps > 0) ? '^' : 'v', d, e_density, esteps); + + // Now save the current position t as the "last position" + // for our next call + memcpy(last_position, t, AXIS_COUNT * sizeof(int32_t)); + } + break; + + // Set the current position + case HOST_CMD_SET_POSITION_EXT : + last_position[0] = cmd->t.set_position_ext.x; + last_position[0] = cmd->t.set_position_ext.y; + last_position[0] = cmd->t.set_position_ext.z; + last_position[0] = cmd->t.set_position_ext.a; + last_position[0] = cmd->t.set_position_ext.b; + break; + } + + return; +} + +int main(int argc, const char *argv[]) +{ + char c; + s3g_context_t *ctx; + s3g_command_t cmd; + int do_edensity; + + do_edensity = 0; + while ((c = getopt(argc, (char **)argv, ":hE?")) != -1) + { + switch(c) + { + // Unknown switch + case ':' : + default : + usage(stderr, argv[0]); + return(1); + + // Explicit help request + case 'h' : + case '?' : + usage(stdout, argv[0]); + return(0); + + // -E print e stepper density + case 'E' : + do_edensity = -1; + break; + } + } + + // Not that we care at this point + argc -= optind; + argv += optind; + + if (argc == 0) + ctx = s3g_open(0, NULL); + else + ctx = s3g_open(0, (void *)argv[0]); + + if (!ctx) + // Assume that s3g_open() has complained + return(1); + + while (!s3g_command_read(ctx, &cmd)) + { + if (do_edensity == 0) + printf("%s (%d)\n", cmd.cmd_name, cmd.cmd_id); + else + edensity(&cmd); + } + + s3g_close(ctx); + + return(0); +} diff --git a/firmware/src/Extruder/EepromDefaults.hh b/firmware/src/Extruder/EepromDefaults.hh new file mode 100644 index 0000000..9c309a5 --- /dev/null +++ b/firmware/src/Extruder/EepromDefaults.hh @@ -0,0 +1 @@ +//Empty file, needed for compilation diff --git a/firmware/src/Extruder/Host.cc b/firmware/src/Extruder/Host.cc index 0d0785f..7e21b2c 100644 --- a/firmware/src/Extruder/Host.cc +++ b/firmware/src/Extruder/Host.cc @@ -222,6 +222,7 @@ bool processQueryPacket(const InPacket& from_host, OutPacket& to_host) { to_host.append16(board.getPlatformHeater().getPIDErrorTerm()); to_host.append16(board.getPlatformHeater().getPIDDeltaTerm()); to_host.append16(board.getPlatformHeater().getPIDLastOutput()); + return true; case SLAVE_CMD_GET_MOTOR_1_RPM: to_host.append8(RC_OK); to_host.append32(motor.getRPMSpeed()); diff --git a/firmware/src/Motherboard/Command.cc b/firmware/src/Motherboard/Command.cc index 736bd59..56af46b 100644 --- a/firmware/src/Motherboard/Command.cc +++ b/firmware/src/Motherboard/Command.cc @@ -15,17 +15,19 @@ * along with this program. If not, see */ +#include "Configuration.hh" #include "Command.hh" #include "Steppers.hh" #include "Commands.hh" +#include "Host.hh" #include "Tool.hh" -#include "Configuration.hh" #include "Timeout.hh" #include "CircularBuffer.hh" #include #include #include "EepromMap.hh" #include "Eeprom.hh" +#include "EepromDefaults.hh" #include "SDCard.hh" #include "ExtruderControl.hh" @@ -35,7 +37,11 @@ namespace command { -#define COMMAND_BUFFER_SIZE 512 +#ifdef SMALL_4K_RAM + #define COMMAND_BUFFER_SIZE 256 +#else + #define COMMAND_BUFFER_SIZE 512 +#endif uint8_t buffer_data[COMMAND_BUFFER_SIZE]; CircularBuffer command_buffer(COMMAND_BUFFER_SIZE, buffer_data); @@ -46,16 +52,25 @@ bool paused = false; uint16_t statusDivisor = 0; volatile uint32_t recentCommandClock = 0; volatile uint32_t recentCommandTime = 0; + +#ifdef PAUSEATZPOS volatile int32_t pauseZPos = 0; +#endif bool estimating = false; + +#ifdef HAS_BUILD_ESTIMATION int64_t estimateTimeUs = 0; +#endif + +#ifdef HAS_FILAMENT_COUNTER volatile int64_t filamentLength = 0; //This maybe pos or neg, but ABS it and all is good (in steps) volatile int64_t lastFilamentLength = 0; volatile bool firstHeatTool0; volatile bool firstHeatHbp; +#endif -Point lastPosition; +static Point lastPosition; uint16_t getRemainingCapacity() { uint16_t sz; @@ -73,6 +88,8 @@ bool isPaused() { return paused; } +#ifdef PAUSEATZPOS + void pauseAtZPos(int32_t zpos) { pauseZPos = zpos; } @@ -81,6 +98,8 @@ int32_t getPauseAtZPos() { return pauseZPos; } +#endif + bool isEmpty() { return command_buffer.isEmpty(); } @@ -136,22 +155,35 @@ Timeout tool_wait_timeout; bool acceleration; void reset() { +#ifdef PAUSEATZPOS pauseAtZPos(0.0); - lastPosition = Point(0,0,0,0,0); +#endif + lastPosition[0] = 0; + lastPosition[1] = 0; + lastPosition[2] = 0; + lastPosition[3] = 0; + lastPosition[4] = 0; command_buffer.reset(); +#ifdef HAS_BUILD_ESTIMATION estimateTimeUs = 0; +#endif + +#ifdef HAS_FILAMENT_COUNTER filamentLength = 0; lastFilamentLength = 0; firstHeatTool0 = true; firstHeatHbp = true; +#endif mode = READY; - uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, EEPROM_DEFAULT_STEPPER_DRIVER); if ( accel & 0x01 ) acceleration = true; else acceleration = false; } +#ifdef HAS_FILAMENT_COUNTER + //Adds the filament used during this build void addFilamentUsed() { @@ -159,11 +191,9 @@ void addFilamentUsed() { int64_t fl = getFilamentLength(); if ( fl > 0 ) { - cli(); - int64_t filamentUsed = eeprom::getEepromInt64(eeprom::FILAMENT_USED, 0); + int64_t filamentUsed = eeprom::getEepromInt64(eeprom::FILAMENT_USED, EEPROM_DEFAULT_FILAMENT_USED); filamentUsed += fl; eeprom::putEepromInt64(eeprom::FILAMENT_USED, filamentUsed); - sei(); //We've used it up, so reset it lastFilamentLength = filamentLength; @@ -171,6 +201,8 @@ void addFilamentUsed() { } } +#endif + //Executes a slave command //Returns true if everything is okay, otherwise false in case of error @@ -223,6 +255,8 @@ bool isPlatformReady() { return false; } +#ifdef HAS_FILAMENT_COUNTER + int64_t getFilamentLength() { if ( filamentLength < 0 ) return -filamentLength; return filamentLength; @@ -231,8 +265,13 @@ int64_t getFilamentLength() { int64_t getLastFilamentLength() { if ( lastFilamentLength < 0 ) return -lastFilamentLength; return lastFilamentLength; + } +#endif + +#ifdef HAS_BUILD_ESTIMATION + int32_t estimateSeconds() { return estimateTimeUs / 1000000; } @@ -249,25 +288,37 @@ void setEstimation(bool on) { } estimateTimeUs = 0; +#ifdef HAS_FILAMENT_COUNTER filamentLength = 0; +#endif estimating = on; } +#endif + void buildAnotherCopy() { +#ifdef HAS_BUILD_ESTIMATION if ( estimating ) setEstimation(false); +#endif recentCommandClock = 0; recentCommandTime = 0; command_buffer.reset(); sdcard::playbackRestart(); +#ifdef HAS_BUILD_ESTIMATION estimateTimeUs = 0; firstHeatTool0 = true; firstHeatHbp = true; +#endif +#ifdef HAS_FILAMENT_COUNTER addFilamentUsed(); lastFilamentLength = 0; +#endif } +#ifdef HAS_BUILD_ESTIMATION + void estimateDelay(uint32_t microseconds) { estimateTimeUs += (int64_t)microseconds; } @@ -295,10 +346,12 @@ void estimateMoveTo(Point p, int32_t dda) { estimateTimeUs += (int64_t)max * (int64_t)dda; +#ifdef HAS_FILAMENT_COUNTER if ( ! estimating ) { filamentLength += (int64_t)(p[3] - lastPosition[3]); filamentLength += (int64_t)(p[4] - lastPosition[4]); } +#endif //Setup lastPosition as the current target for ( uint8_t i = 0; i < AXIS_COUNT; i ++ ) lastPosition[i] = p[i]; @@ -312,16 +365,22 @@ void estimateMoveToNew(Point p, int32_t us, uint8_t relative) { if ( relative & (1 << i)) { if (( ! estimating ) && (( i == 3 ) || ( i == 4 ))) +#ifdef HAS_FILAMENT_COUNTER filamentLength += (int64_t)p[i]; +#endif lastPosition[i] += p[i]; } else { if (( ! estimating ) && (( i == 3 ) || ( i == 4 ))) +#ifdef HAS_FILAMENT_COUNTER filamentLength += (int64_t)(p[i] - lastPosition[i]); +#endif lastPosition[i] = p[i]; } } } +#endif + // A fast slice for processing commands and refilling the stepper queue, etc. void runCommandSlice() { recentCommandClock ++; @@ -337,11 +396,14 @@ void runCommandSlice() { } if ((paused) && ( ! estimating )) { return; } +#ifdef PAUSEATZPOS //If we've reached Pause @ ZPos, then pause if ((( pauseZPos != 0) && ( ! isPaused() ) && ( steppers::getPosition()[2]) >= pauseZPos ) && ( ! estimating )) pause(true); +#endif +#ifdef HAS_BUILD_ESTIMATION //If we're estimating, we don't need to wait for anything if (( estimating ) && (( mode == HOMING ) || @@ -350,6 +412,7 @@ void runCommandSlice() { ( mode == WAIT_ON_TOOL ) || ( mode == WAIT_ON_PLATFORM ))) mode = READY; +#endif if (mode == HOMING) { if (!steppers::isRunning()) { @@ -395,7 +458,15 @@ void runCommandSlice() { //by waiting for the pipeline buffer to empty before continuing if ((command != HOST_CMD_QUEUE_POINT_ABS) && (command != HOST_CMD_QUEUE_POINT_EXT) && - (command != HOST_CMD_QUEUE_POINT_NEW)) { + (command != HOST_CMD_QUEUE_POINT_NEW) && +#ifndef CMD_SET_POSITION_CAUSES_DRAIN + (command != HOST_CMD_SET_POSITION) && + (command != HOST_CMD_SET_POSITION_EXT) && +#endif + ((command != HOST_CMD_TOOL_COMMAND) || + (command_buffer.getLength() < 5 ) || + ((command_buffer[2] != SLAVE_CMD_TOGGLE_VALVE) && + (command_buffer[2] != SLAVE_CMD_TOGGLE_MOTOR_1)))) { if ( ! st_empty() ) return; } } @@ -418,7 +489,9 @@ void runCommandSlice() { int32_t y = pop32(); int32_t z = pop32(); int32_t dda = pop32(); +#ifdef HAS_BUILD_ESTIMATION estimateMoveTo(Point(x,y,z),dda); +#endif if ( ! estimating ) steppers::setTarget(Point(x,y,z),dda); } } else if (command == HOST_CMD_QUEUE_POINT_EXT) { @@ -433,7 +506,9 @@ void runCommandSlice() { int32_t a = pop32(); int32_t b = pop32(); int32_t dda = pop32(); +#ifdef HAS_BUILD_ESTIMATION estimateMoveTo(Point(x,y,z,a,b),dda); +#endif if ( ! estimating ) steppers::setTarget(Point(x,y,z,a,b),dda); } } else if (command == HOST_CMD_QUEUE_POINT_NEW) { @@ -449,7 +524,9 @@ void runCommandSlice() { int32_t b = pop32(); int32_t us = pop32(); uint8_t relative = pop8(); +#ifdef HAS_BUILD_ESTIMATION estimateMoveToNew(Point(x,y,z,a,b),us,relative); +#endif if ( ! estimating ) steppers::setTargetNew(Point(x,y,z,a,b),us,relative); } } else if (command == HOST_CMD_CHANGE_TOOL) { @@ -477,7 +554,9 @@ void runCommandSlice() { int32_t x = pop32(); int32_t y = pop32(); int32_t z = pop32(); +#ifdef HAS_BUILD_ESTIMATION estimateDefinePosition(Point(x,y,z)); +#endif if ( ! estimating ) steppers::definePosition(Point(x,y,z)); } } else if (command == HOST_CMD_SET_POSITION_EXT) { @@ -489,7 +568,9 @@ void runCommandSlice() { int32_t z = pop32(); int32_t a = pop32(); int32_t b = pop32(); +#ifdef HAS_BUILD_ESTIMATION estimateDefinePosition(Point(x,y,z,a,b)); +#endif if ( ! estimating ) steppers::definePosition(Point(x,y,z,a,b)); } } else if (command == HOST_CMD_DELAY) { @@ -498,7 +579,9 @@ void runCommandSlice() { command_buffer.pop(); // remove the command code // parameter is in milliseconds; timeouts need microseconds uint32_t microseconds = pop32() * 1000; +#ifdef HAS_BUILD_ESTIMATION estimateDelay(microseconds); +#endif if ( ! estimating ) delay_timeout.start(microseconds); } } else if (command == HOST_CMD_FIND_AXES_MINIMUM || @@ -573,7 +656,9 @@ void runCommandSlice() { } } +#ifdef HAS_BUILD_ESTIMATION estimateDefinePosition(newPoint); +#endif if ( ! estimating ) steppers::definePosition(newPoint); } @@ -595,6 +680,7 @@ void runCommandSlice() { command_buffer.pop(); // remove the command code out.append8(command_buffer.pop()); // copy tool index uint8_t commandCode = command_buffer.pop(); + out.append8(commandCode); // copy command code int len = pop8(); // get payload length @@ -603,29 +689,32 @@ void runCommandSlice() { for (int i = 0; (i < len) && ( i < 4); i ++) buf[i] = command_buffer.pop(); - if (( commandCode == SLAVE_CMD_SET_TEMP ) && ( ! estimating ) && - ( ! sdcard::isPlaying()) ) { +#ifdef HAS_FILAMENT_COUNTER + if (( commandCode == SLAVE_CMD_SET_TEMP ) && ( ! sdcard::isPlaying()) ) { uint16_t *temp = (uint16_t *)&buf[0]; if ( *temp == 0 ) addFilamentUsed(); } +#endif +#ifdef HAS_BUILD_ESTIMATION uint8_t overrideTemp = 0; if ( commandCode == SLAVE_CMD_SET_TEMP ) { uint16_t *temp = (uint16_t *)&buf[0]; - if (( *temp != 0 ) && ( firstHeatTool0 ) && ( eeprom::getEeprom8(eeprom::OVERRIDE_GCODE_TEMP, 0) )) { + if (( *temp != 0 ) && ( firstHeatTool0 ) && ( eeprom::getEeprom8(eeprom::OVERRIDE_GCODE_TEMP, EEPROM_DEFAULT_OVERRIDE_GCODE_TEMP) )) { firstHeatTool0 = false; - overrideTemp = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220); + overrideTemp = eeprom::getEeprom8(eeprom::TOOL0_TEMP, EEPROM_DEFAULT_TOOL0_TEMP); *temp = overrideTemp; } } if ( commandCode == SLAVE_CMD_SET_PLATFORM_TEMP ) { uint16_t *temp = (uint16_t *)&buf[0]; - if (( *temp != 0 ) && ( firstHeatHbp ) && ( eeprom::getEeprom8(eeprom::OVERRIDE_GCODE_TEMP, 0) )) { + if (( *temp != 0 ) && ( firstHeatHbp ) && ( eeprom::getEeprom8(eeprom::OVERRIDE_GCODE_TEMP, EEPROM_DEFAULT_OVERRIDE_GCODE_TEMP) )) { firstHeatHbp = false; - overrideTemp = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110); + overrideTemp = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, EEPROM_DEFAULT_PLATFORM_TEMP); *temp = overrideTemp; } } +#endif for (int i = 0; (i < len) && ( i < 4); i ++) out.append8(buf[i]); @@ -633,14 +722,114 @@ void runCommandSlice() { for (int i = 4; i < len; i ++ ) out.append8(command_buffer.pop()); + if (commandCode == SLAVE_CMD_TOGGLE_VALVE) { + uint8_t valveState = (len > 0) ? *((uint8_t *)&buf[0]) : 1; + steppers::setSegmentAccelState(valveState != 0); + } + else tool::startTransaction(); + // we don't care about the response, so we can release // the lock after we initiate the transfer - tool::startTransaction(); tool::releaseLock(); } } } } + } else if ( command == HOST_CMD_SET_MAX_ACCEL ) { + if (command_buffer.getLength() >= 17) { + command_buffer.pop(); // remove the command code + int32_t x = pop32(); + int32_t y = pop32(); + int32_t z = pop32(); + int32_t a = pop32(); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X,(uint32_t)x); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y,(uint32_t)y); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z,(uint32_t)z); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A,(uint32_t)a); + } + } else if ( command == HOST_CMD_SET_MAX_FEEDRATE ) { + if (command_buffer.getLength() >= 17) { + command_buffer.pop(); // remove the command code + int32_t x = pop32(); + int32_t y = pop32(); + int32_t z = pop32(); + int32_t a = pop32(); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X,(uint32_t)x); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y,(uint32_t)y); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z,(uint32_t)z); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A,(uint32_t)a); + } + } else if ( command == HOST_CMD_SET_DEFAULT_ACCEL ) { + if (command_buffer.getLength() >= 9) { + command_buffer.pop(); // remove the command code + int32_t s = pop32(); + int32_t t = pop32(); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM,(uint32_t)s); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT,(uint32_t)t); + } + } else if ( command == HOST_CMD_SET_ADVANCED_ACCEL ) { + if (command_buffer.getLength() >= 13) { + command_buffer.pop(); // remove the command code + int32_t s = pop32(); + int32_t t = pop32(); + int32_t z = pop32(); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,(uint32_t)s); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,(uint32_t)t); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_PLANNER_SPEED,(uint32_t)z); + } + } else if ( command == HOST_CMD_SET_ADVANCED_ACCEL2 ) { + uint16_t len = command_buffer.getLength(); + if (len >= 5) { + command_buffer.pop(); // remove the command code + int32_t s = pop32(); + eeprom::putEepromUInt32(eeprom::ACCEL_NOODLE_DIAMETER,(uint32_t)s); + + if (len >= 17) { + int32_t a = pop32(); + int32_t k = pop32(); + int32_t x = pop32(); + int32_t y = pop32(); + eeprom::putEepromUInt32(eeprom::ACCEL_REV_MAX_FEED_RATE,(uint32_t)a); + eeprom::putEepromUInt32(eeprom::ACCEL_EXTRUDER_DEPRIME,(uint32_t)k); + eeprom::putEepromUInt32(eeprom::ACCEL_SLOWDOWN_LIMIT,(uint32_t)x); + eeprom::putEepromUInt32(eeprom::ACCEL_CLOCKWISE_EXTRUDER,(uint32_t)y); + } + } + } else if ( command == HOST_CMD_SET_ADVANCE_K ) { + if (command_buffer.getLength() >= 13) { + command_buffer.pop(); // remove the command code + int32_t s = pop32(); + int32_t a = pop32(); + int32_t k = pop32(); + eeprom::putEepromUInt32(eeprom::ACCEL_ADVANCE_K,(uint32_t)s); + eeprom::putEepromUInt32(eeprom::ACCEL_ADVANCE_K2,(uint32_t)a); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_SEGMENT_TIME,(uint32_t)k); + } + } else if ( command == HOST_CMD_SET_EXTRUDER_STEPSMM ) { + if (command_buffer.getLength() >= 5) { + command_buffer.pop(); // remove the command code + int32_t a = pop32(); + eeprom::putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,(uint32_t)a); + } + } else if ( command == HOST_CMD_SET_ACCELERATION ) { + if (command_buffer.getLength() >= 2) { + command_buffer.pop(); // remove the command code + uint8_t s = pop8(); + eeprom_write_byte((uint8_t*)eeprom::STEPPER_DRIVER,s); + eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,0); + } + } else if ( command == HOST_CMD_SET_MAX_SPEED_CHANGE ) { + if (command_buffer.getLength() >= 17) { + command_buffer.pop(); // remove the command code + int32_t x = pop32(); + int32_t y = pop32(); + int32_t z = pop32(); + int32_t a = pop32(); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_X,(uint32_t)x); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Y,(uint32_t)y); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Z,(uint32_t)z); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_A,(uint32_t)a); + } } else if (command == HOST_CMD_MOOD_LIGHT_SET_RGB ) { // check for completion if (command_buffer.getLength() >= 21) { @@ -681,15 +870,16 @@ void runCommandSlice() { // check for completion if (command_buffer.getLength() >= 2) { command_buffer.pop(); // remove the command code + uint8_t repeats = pop8(); if ( ! estimating ) { cli(); - eeprom_write_byte((uint8_t*)eeprom::BUZZER_REPEATS, pop8()); + eeprom_write_byte((uint8_t*)eeprom::BUZZER_REPEATS, repeats); sei(); } } } else if (command == HOST_CMD_BUZZER_BUZZ ) { // check for completion - if (command_buffer.getLength() >= 7) { + if (command_buffer.getLength() >= 4) { command_buffer.pop(); // remove the command code uint8_t buzzes = pop8(); uint8_t duration = pop8(); @@ -701,6 +891,26 @@ void runCommandSlice() { } #endif } + } else if ( command == HOST_CMD_SET_AXIS_STEPS_MM) { + if (command_buffer.getLength() >= 17) { + command_buffer.pop(); // remove the command code + int64_t x = (int64_t)pop32() * 10000; + int64_t y = (int64_t)pop32() * 10000; + int64_t z = (int64_t)pop32() * 10000; + int64_t a = (int64_t)pop32() * 10000; + cli(); + eeprom_write_block(&x,(void *)eeprom::STEPS_PER_MM_X,8); + eeprom_write_block(&y,(void *)eeprom::STEPS_PER_MM_Y,8); + eeprom_write_block(&z,(void *)eeprom::STEPS_PER_MM_Z,8); + eeprom_write_block(&a,(void *)eeprom::STEPS_PER_MM_A,8); + sei(); + } + } else if (command == HOST_CMD_RESET_TO_FACTORY) { + pop16(); // remove the command and following reserved byte + cli(); + eeprom::setDefaults(true); + sei(); + host::prepReset(); } else { } } diff --git a/firmware/src/Motherboard/Command.hh b/firmware/src/Motherboard/Command.hh index deb01bd..7090bc4 100644 --- a/firmware/src/Motherboard/Command.hh +++ b/firmware/src/Motherboard/Command.hh @@ -28,8 +28,10 @@ namespace command { /// commands. void reset(); +#ifdef HAS_FILAMENT_COUNTER /// Adds the filament used in this build to eeprom void addFilamentUsed(); +#endif /// Run the command thread slice. void runCommandSlice(); @@ -44,6 +46,8 @@ void pause(bool pause); /// \return True if it is disabled, false if it is enabled. bool isPaused(); +#ifdef PAUSEATZPOS + /// \Pause at >= a Z Position provded in steps /// 0 cancels pauseAtZPos void pauseAtZPos(int32_t zpos); @@ -52,6 +56,10 @@ void pauseAtZPos(int32_t zpos); /// \return the z position set for pausing (in steps), otherwise 0 int32_t getPauseAtZPos(); +#endif + +#ifdef HAS_FILAMENT_COUNTER + /// Returns the length of filament extruded (in steps) int64_t getFilamentLength(); @@ -59,12 +67,18 @@ int64_t getFilamentLength(); /// last time the filament was added to the filament count int64_t getLastFilamentLength(); +#endif + +#ifdef HAS_BUILD_ESTIMATION + //Returns the number of seconds estimated int32_t estimateSeconds(); //Set the estimation mode void setEstimation(bool on); +#endif + //Build another copy void buildAnotherCopy(); diff --git a/firmware/src/Motherboard/EepromMap.cc b/firmware/src/Motherboard/EepromMap.cc index 4478987..ec544f4 100644 --- a/firmware/src/Motherboard/EepromMap.cc +++ b/firmware/src/Motherboard/EepromMap.cc @@ -17,59 +17,156 @@ #include "EepromMap.hh" #include "Eeprom.hh" +#include "EepromDefaults.hh" #include namespace eeprom { +void setJettyFirmwareDefaults() { +#ifdef ERASE_EEPROM_ON_EVERY_BOOT + return; +#endif + +#ifdef EEPROM_DEFAULT_TOOL0_TEMP + eeprom_write_byte((uint8_t*)eeprom::TOOL0_TEMP, EEPROM_DEFAULT_TOOL0_TEMP); +#endif + +#ifdef EEPROM_DEFAULT_TOOL1_TEMP + eeprom_write_byte((uint8_t*)eeprom::TOOL1_TEMP, EEPROM_DEFAULT_TOOL1_TEMP); +#endif + +#ifdef EEPROM_DEFAULT_PLATFORM_TEMP + eeprom_write_byte((uint8_t*)eeprom::PLATFORM_TEMP, EEPROM_DEFAULT_PLATFORM_TEMP); +#endif + +#ifdef EEPROM_DEFAULT_EXTRUDE_DURATION + eeprom_write_byte((uint8_t*)eeprom::EXTRUDE_DURATION, EEPROM_DEFAULT_EXTRUDE_DURATION); +#endif + +#ifdef EEPROM_DEFAULT_EXTRUDE_MMS + eeprom_write_byte((uint8_t*)eeprom::EXTRUDE_MMS, EEPROM_DEFAULT_EXTRUDE_MMS); +#endif + +#ifdef EEPROM_DEFAULT_MOOD_LIGHT_SCRIPT + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_SCRIPT, EEPROM_DEFAULT_MOOD_LIGHT_SCRIPT); +#endif + +#ifdef EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_RED + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_RED, EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_RED); +#endif + +#ifdef EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_GREEN + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_GREEN, EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_GREEN); +#endif + +#ifdef EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_BLUE + eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_BLUE, EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_BLUE); +#endif + + eeprom_write_byte((uint8_t*)eeprom::JOG_MODE_SETTINGS, EEPROM_DEFAULT_JOG_MODE_SETTINGS); + +#ifdef EEPROM_DEFAULT_BUZZER_REPEATS + eeprom_write_byte((uint8_t*)eeprom::BUZZER_REPEATS, EEPROM_DEFAULT_BUZZER_REPEATS); +#endif + + putEepromInt64(eeprom::STEPS_PER_MM_X, EEPROM_DEFAULT_STEPS_PER_MM_X); + putEepromInt64(eeprom::STEPS_PER_MM_Y, EEPROM_DEFAULT_STEPS_PER_MM_Y); + putEepromInt64(eeprom::STEPS_PER_MM_Z, EEPROM_DEFAULT_STEPS_PER_MM_Z); + putEepromInt64(eeprom::STEPS_PER_MM_A, EEPROM_DEFAULT_STEPS_PER_MM_A); + putEepromInt64(eeprom::STEPS_PER_MM_B, EEPROM_DEFAULT_STEPS_PER_MM_B); + +#ifdef EEPROM_DEFAULT_ABP_COPIES + eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES, EEPROM_DEFAULT_ABP_COPIES); +#endif + +#ifdef EEPROM_DEFAULT_PREHEAT_DURING_ESTIMATE + eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE, EEPROM_DEFAULT_PREHEAT_DURING_ESTIMATE); +#endif + +#ifdef EEPROM_DEFAULT_OVERRIDE_GCODE_TEMP + eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP, EEPROM_DEFAULT_OVERRIDE_GCODE_TEMP); +#endif + + eeprom_write_byte((uint8_t*)eeprom::STEPPER_DRIVER, EEPROM_DEFAULT_STEPPER_DRIVER); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Z); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_A); + putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_B, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_B); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_X); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Y); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Z); + putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_A); + putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_NORM); + putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_RETRACT); + putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM); + putEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE, EEPROM_DEFAULT_ACCEL_MIN_FEED_RATE); + putEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE, EEPROM_DEFAULT_ACCEL_MIN_TRAVEL_FEED_RATE); + putEepromUInt32(eeprom::ACCEL_ADVANCE_K2, EEPROM_DEFAULT_ACCEL_ADVANCE_K2); + putEepromUInt32(eeprom::ACCEL_MIN_PLANNER_SPEED, EEPROM_DEFAULT_ACCEL_MIN_PLANNER_SPEED); + putEepromUInt32(eeprom::ACCEL_ADVANCE_K, EEPROM_DEFAULT_ACCEL_ADVANCE_K); + putEepromUInt32(eeprom::ACCEL_NOODLE_DIAMETER, EEPROM_DEFAULT_ACCEL_NOODLE_DIAMETER); + putEepromUInt32(eeprom::ACCEL_MIN_SEGMENT_TIME, EEPROM_DEFAULT_ACCEL_MIN_SEGMENT_TIME); + +#ifdef EEPROM_DEFAULT_LCD_TYPE + eeprom_write_byte((uint8_t*)eeprom::LCD_TYPE, EEPROM_DEFAULT_LCD_TYPE); +#endif + +#ifdef EEPROM_DEFAULT_ENDSTOPS_USED + eeprom_write_byte((uint8_t*)eeprom::ENDSTOPS_USED, EEPROM_DEFAULT_ENDSTOPS_USED); +#endif + +#ifdef EEPROM_DEFAULT_HOMING_FEED_RATE_X + putEepromUInt32(eeprom::HOMING_FEED_RATE_X, EEPROM_DEFAULT_HOMING_FEED_RATE_X); +#endif + +#ifdef EEPROM_DEFAULT_HOMING_FEED_RATE_Y + putEepromUInt32(eeprom::HOMING_FEED_RATE_Y, EEPROM_DEFAULT_HOMING_FEED_RATE_Y); +#endif + +#ifdef EEPROM_DEFAULT_HOMING_FEED_RATE_Z + putEepromUInt32(eeprom::HOMING_FEED_RATE_Z, EEPROM_DEFAULT_HOMING_FEED_RATE_Z); +#endif + + putEepromUInt32(eeprom::ACCEL_REV_MAX_FEED_RATE, EEPROM_DEFAULT_ACCEL_REV_MAX_FEED_RATE); + putEepromUInt32(eeprom::ACCEL_EXTRUDER_DEPRIME, EEPROM_DEFAULT_ACCEL_EXTRUDER_DEPRIME); + putEepromUInt32(eeprom::ACCEL_SLOWDOWN_LIMIT, EEPROM_DEFAULT_ACCEL_SLOWDOWN_LIMIT); + putEepromUInt32(eeprom::ACCEL_CLOCKWISE_EXTRUDER, EEPROM_DEFAULT_ACCEL_CLOCKWISE_EXTRUDER); + +#ifdef EEPROM_DEFAULT_INVERTED_EXTRUDER_5D + eeprom_write_byte((uint8_t*)eeprom::INVERTED_EXTRUDER_5D, EEPROM_DEFAULT_INVERTED_EXTRUDER_5D); +#endif + + putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_X, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_X); + putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Y, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Y); + putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Z, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Z); + putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_A, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_A); +} + // TODO: Shouldn't this just reset everything to an uninitialized state? -void setDefaults() { +void setDefaults(bool retainCounters) { +#ifdef ERASE_EEPROM_ON_EVERY_BOOT + return; +#endif + // Initialize eeprom map - // Default: enstops inverted, Y axis inverted - uint8_t axis_invert = 1<<1; // Y axis = 1 - uint8_t endstop_invert = 0b00011111; // all endstops inverted - eeprom_write_byte((uint8_t*)eeprom::AXIS_INVERSION,axis_invert); - eeprom_write_byte((uint8_t*)eeprom::ENDSTOP_INVERSION,endstop_invert); - eeprom_write_byte((uint8_t*)eeprom::MACHINE_NAME,0); // name is null - eeprom_write_byte((uint8_t*)eeprom::TOOL0_TEMP,220); - eeprom_write_byte((uint8_t*)eeprom::TOOL1_TEMP,220); - eeprom_write_byte((uint8_t*)eeprom::PLATFORM_TEMP,110); - eeprom_write_byte((uint8_t*)eeprom::EXTRUDE_DURATION,1); - eeprom_write_byte((uint8_t*)eeprom::EXTRUDE_RPM,19); - eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_SCRIPT,0); - eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_RED,255); - eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_GREEN,255); - eeprom_write_byte((uint8_t*)eeprom::MOOD_LIGHT_CUSTOM_BLUE,255); - eeprom_write_byte((uint8_t*)eeprom::JOG_MODE_SETTINGS,0); - eeprom_write_byte((uint8_t*)eeprom::BUZZER_REPEATS,3); - putEepromInt64(eeprom::STEPS_PER_MM_X,STEPS_PER_MM_X_DEFAULT); - putEepromInt64(eeprom::STEPS_PER_MM_Y,STEPS_PER_MM_Y_DEFAULT); - putEepromInt64(eeprom::STEPS_PER_MM_Z,STEPS_PER_MM_Z_DEFAULT); - putEepromInt64(eeprom::STEPS_PER_MM_A,STEPS_PER_MM_A_DEFAULT); - putEepromInt64(eeprom::STEPS_PER_MM_B,STEPS_PER_MM_B_DEFAULT); - putEepromInt64(eeprom::FILAMENT_USED,0); - putEepromInt64(eeprom::FILAMENT_USED_TRIP,0); - eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,1); - eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,0); - eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,0); - eeprom_write_byte((uint8_t*)eeprom::STEPPER_DRIVER,0); - putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X,160); - putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y,160); - putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z,10); - putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A,100); - putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_B,100); - putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X,2000); - putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y,2000); - putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z,150); - putEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A,60000); - putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM,5000); - putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT,3000); - putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,44); //Multiplied by 10 - putEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,0); //Multiplied by 10 - putEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,0); //Multiplied by 10 - putEepromUInt32(eeprom::ACCEL_MAX_XY_JERK,2); //30mm/s Multiplied by 10 - putEepromUInt32(eeprom::ACCEL_MAX_Z_JERK,100); //10mm/s Multiplied by 10 - putEepromUInt32(eeprom::ACCEL_ADVANCE_K,50); //0.00001 Multiplied by 100000 - putEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER,175); //1.75 Multiplied by 100 + eeprom_write_byte((uint8_t*)eeprom::AXIS_INVERSION, EEPROM_DEFAULT_AXIS_INVERSION); + eeprom_write_byte((uint8_t*)eeprom::ENDSTOP_INVERSION, EEPROM_DEFAULT_ENDSTOP_INVERSION); + eeprom_write_byte((uint8_t*)eeprom::MACHINE_NAME, EEPROM_DEFAULT_MACHINE_NAME); + +#if defined(EEPROM_DEFAULT_ESTOP_CONFIGURATION) && defined(HAS_ESTOP) + eeprom_write_byte((uint8_t*)eeprom::ESTOP_CONFIGURATION, EEPROM_DEFAULT_ESTOP_CONFIGURATION); +#endif + + setJettyFirmwareDefaults(); + + if (retainCounters) + return; + + //These settings aren't set in Jetty Firmware defaults, as we never want them to be accidently overwritten + putEepromInt64(eeprom::FILAMENT_USED, EEPROM_DEFAULT_FILAMENT_USED); + putEepromInt64(eeprom::FILAMENT_USED_TRIP, EEPROM_DEFAULT_FILAMENT_USED_TRIP); + } } diff --git a/firmware/src/Motherboard/EepromMap.hh b/firmware/src/Motherboard/EepromMap.hh index c91458e..3ca1348 100644 --- a/firmware/src/Motherboard/EepromMap.hh +++ b/firmware/src/Motherboard/EepromMap.hh @@ -26,13 +26,20 @@ namespace eeprom { const static uint16_t EEPROM_SIZE = 0x0200; /// Version, low byte: 1 byte +//$BEGIN_ENTRY +//$type:B const static uint16_t VERSION_LOW = 0x0000; + /// Version, high byte: 1 byte +//$BEGIN_ENTRY +//$type:B const static uint16_t VERSION_HIGH = 0x0001; /// Axis inversion flags: 1 byte. /// Axis N (where X=0, Y=1, etc.) is inverted if the Nth bit is set. /// Bit 7 is used for HoldZ OFF: 1 = off, 0 = on +//$BEGIN_ENTRY +//$type:B const static uint16_t AXIS_INVERSION = 0x0002; /// Endstop inversion flags: 1 byte. @@ -41,15 +48,23 @@ const static uint16_t AXIS_INVERSION = 0x0002; /// Bit 7 is set to indicate endstops are present; it is zero to indicate /// that endstops are not present. /// Ordinary endstops (H21LOB et. al.) are inverted. +//$BEGIN_ENTRY +//$type:B const static uint16_t ENDSTOP_INVERSION = 0x0003; -/// Name of this machine: 32 bytes. +/// Name of this machine: 32 bytes +//$BEGIN_ENTRY +//$type:s $length:32 const static uint16_t MACHINE_NAME = 0x0020; /// Default locations for the axis: 5 x 32 bit = 20 bytes +//$BEGIN_ENTRY +//$type:iiiii const static uint16_t AXIS_HOME_POSITIONS = 0x0060; // Estop configuration byte: 1 byte. +//$BEGIN_ENTRY +//$type:B const static uint16_t ESTOP_CONFIGURATION = 0x0074; enum { @@ -58,42 +73,100 @@ enum { ESTOP_CONF_ACTIVE_LOW = 0x2 }; +//$BEGIN_ENTRY +//$type:B const static uint16_t TOOL0_TEMP = 0x0080; + +//$BEGIN_ENTRY +//$type:B const static uint16_t TOOL1_TEMP = 0x0081; + +//$BEGIN_ENTRY +//$type:B const static uint16_t PLATFORM_TEMP = 0x0082; + +//$BEGIN_ENTRY +//$type:B const static uint16_t EXTRUDE_DURATION = 0x0083; -const static uint16_t EXTRUDE_RPM = 0x0084; + +//$BEGIN_ENTRY +//$type:B +const static uint16_t EXTRUDE_MMS = 0x0084; + +//$BEGIN_ENTRY +//$type:B const static uint16_t MOOD_LIGHT_SCRIPT = 0x0085; + +//$BEGIN_ENTRY +//$type:B const static uint16_t MOOD_LIGHT_CUSTOM_RED = 0x0086; + +//$BEGIN_ENTRY +//$type:B const static uint16_t MOOD_LIGHT_CUSTOM_GREEN = 0x0087; + +//$BEGIN_ENTRY +//$type:B const static uint16_t MOOD_LIGHT_CUSTOM_BLUE = 0x0088; //Bit 1 is Model mode or user view mode (user view mode = bit set) //Bit 2-4 are the jog mode distance 0 = short, 1 = long, 2 = cont +//$BEGIN_ENTRY +//$type:B const static uint16_t JOG_MODE_SETTINGS = 0x0089; //0 = No system buzzing, >=1 = number of repeats to buzz for +//$BEGIN_ENTRY +//$type:B const static uint16_t BUZZER_REPEATS = 0x008A; //Steps per mm, each one is 8 bytes long and are stored as int64_t +//$BEGIN_ENTRY +//$type:q $floating_point:True $exponent:-10 const static uint16_t STEPS_PER_MM_X = 0x008B; + +//Steps per mm, each one is 8 bytes long and are stored as int64_t +//$BEGIN_ENTRY +//$type:q $floating_point:True $exponent:-10 const static uint16_t STEPS_PER_MM_Y = 0x0093; + +//Steps per mm, each one is 8 bytes long and are stored as int64_t +//$BEGIN_ENTRY +//$type:q $floating_point:True $exponent:-10 const static uint16_t STEPS_PER_MM_Z = 0x009B; + +//Steps per mm, each one is 8 bytes long and are stored as int64_t +//$BEGIN_ENTRY +//$type:q $floating_point:True $exponent:-10 const static uint16_t STEPS_PER_MM_A = 0x00A3; + +//Steps per mm, each one is 8 bytes long and are stored as int64_t +//$BEGIN_ENTRY +//$type:q $floating_point:True $exponent:-10 const static uint16_t STEPS_PER_MM_B = 0x00AB; //int64_t (8 bytes) The filament used in steps +//$BEGIN_ENTRY +//$type:q const static uint16_t FILAMENT_USED = 0x00B3; +//$BEGIN_ENTRY +//$type:q const static uint16_t FILAMENT_USED_TRIP = 0x00BB; //Number of ABP copies (1-254) when building from SDCard (1 byte) +//$BEGIN_ENTRY +//$type:B const static uint16_t ABP_COPIES = 0x00C3; //Preheat during estimate 0 = Disable, 1 = Enabled +//$BEGIN_ENTRY +//$type:B const static uint16_t PREHEAT_DURING_ESTIMATE = 0x00C4; //Override the temperature set in the gcode file at the start of the build //0 = Disable, 1 = Enabled +//$BEGIN_ENTRY +//$type:B const static uint16_t OVERRIDE_GCODE_TEMP = 0x00C5; //Profiles @@ -109,38 +182,152 @@ const static uint16_t PROFILE_BASE = 0x00C6; //1 = Accelerated Stepper Driver, 0 = Regular stepper driver (default) //Bit 2 is planner enabled +//Bit 3 is strangle enabled (used to constain axis to Max Speed Change with Non Accelerated Mode) +//$BEGIN_ENTRY +//$type:B const static uint16_t STEPPER_DRIVER = 0x0126; //uint32_t (4 bytes) +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_FEEDRATE_X = 0x0127; +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_FEEDRATE_Y = 0x012B; +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_FEEDRATE_Z = 0x012F; +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_FEEDRATE_A = 0x0133; +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_FEEDRATE_B = 0x0137; //uint32_t (4 bytes) +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_ACCELERATION_X = 0x013B; +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_ACCELERATION_Y = 0x013F; +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_ACCELERATION_Z = 0x0143; +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_ACCELERATION_A = 0x0147; //uint32_t (4 bytes) +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_EXTRUDER_NORM = 0x014B; +//$BEGIN_ENTRY +//$type:I const static uint16_t ACCEL_MAX_EXTRUDER_RETRACT= 0x014F; //uint32_t (4 bytes) +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-1 const static uint16_t ACCEL_E_STEPS_PER_MM = 0x0153; //uint32_t (4 bytes) +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-1 const static uint16_t ACCEL_MIN_FEED_RATE = 0x0157; +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-1 const static uint16_t ACCEL_MIN_TRAVEL_FEED_RATE= 0x015B; -const static uint16_t ACCEL_MAX_XY_JERK = 0x015F; -const static uint16_t ACCEL_MAX_Z_JERK = 0x0163; +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-5 +const static uint16_t ACCEL_ADVANCE_K2 = 0x015F; +//NOTE: Jetty Code uses min planner speed as a float, but it's stored as a uint32_t +//$BEGIN_ENTRY +//$type:I +const static uint16_t ACCEL_MIN_PLANNER_SPEED = 0x0163; +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-5 const static uint16_t ACCEL_ADVANCE_K = 0x0167; -const static uint16_t ACCEL_FILAMENT_DIAMETER = 0x016B; +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-2 +const static uint16_t ACCEL_NOODLE_DIAMETER = 0x016B; +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-4 +const static uint16_t ACCEL_MIN_SEGMENT_TIME = 0x016F; + +//uint8_t (1 byte) +//$BEGIN_ENTRY +//$type:B +const static uint16_t LCD_TYPE = 0x0173; + +//uint8_t (1 byte) +//Bitwise (true = endstop present) +//1 = X Min +//2 = X Max +//4 = Y Min +//8 = Y Max +//16 = Z Min +//32 = Z Max +//$BEGIN_ENTRY +//$type:B +const static uint16_t ENDSTOPS_USED = 0x0174; + +//uint32_t (4 bytes) Homing feed rate in mm/min +//$BEGIN_ENTRY +//$type:I +const static uint16_t HOMING_FEED_RATE_X = 0x0175; +//$BEGIN_ENTRY +//$type:I +const static uint16_t HOMING_FEED_RATE_Y = 0x0179; +//$BEGIN_ENTRY +//$type:I +const static uint16_t HOMING_FEED_RATE_Z = 0x017D; + +//uint32_t (4 bytes) +//$BEGIN_ENTRY +//$type:I +const static uint16_t ACCEL_REV_MAX_FEED_RATE = 0x0181; +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-1 +const static uint16_t ACCEL_EXTRUDER_DEPRIME = 0x0185; +//$BEGIN_ENTRY +//$type:I +const static uint16_t ACCEL_SLOWDOWN_LIMIT = 0x0189; +//$BEGIN_ENTRY +//$type:I +const static uint16_t ACCEL_CLOCKWISE_EXTRUDER = 0x018D; + +//uint8_t (1 byte) +//$BEGIN_ENTRY +//$type:B +const static uint16_t INVERTED_EXTRUDER_5D = 0x0191; + +//uint32_t (4 bytes) +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-1 +const static uint16_t ACCEL_MAX_SPEED_CHANGE_X= 0x0192; +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-1 +const static uint16_t ACCEL_MAX_SPEED_CHANGE_Y= 0x0196; +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-1 +const static uint16_t ACCEL_MAX_SPEED_CHANGE_Z= 0x019A; +//$BEGIN_ENTRY +//$type:I $floating_point:True $exponent:-1 +const static uint16_t ACCEL_MAX_SPEED_CHANGE_A= 0x019E; + +#ifdef STORE_RAM_USAGE_TO_EEPROM +//$BEGIN_ENTRY +//$type:I +const static uint16_t RAM_USAGE_DEBUG = 0x01D0; +#endif + + +/// Reset Jetty Firmware defaults only +void setJettyFirmwareDefaults(); /// Reset all data in the EEPROM to a default. -void setDefaults(); +void setDefaults(bool retainCounters); } // namespace eeprom diff --git a/firmware/src/Motherboard/Errors.hh b/firmware/src/Motherboard/Errors.hh index c47736f..83692d5 100644 --- a/firmware/src/Motherboard/Errors.hh +++ b/firmware/src/Motherboard/Errors.hh @@ -18,6 +18,8 @@ #ifndef ERRORS_HH_ #define ERRORS_HH_ +#include "Configuration.hh" + /// Definition of blink codes for error conditions. // TODO: Add these to the main documentation page. @@ -31,5 +33,8 @@ #define ERR_WDT_TIMEOUT 6 #define ERR_ESTOP 7 #define ERR_HOST_TRUNCATED_CMD 8 +#ifdef SMALL_4K_RAM + #define ERR_NO_FREE_SRAM 9 +#endif #endif /* ERRORS_HH_ */ diff --git a/firmware/src/Motherboard/Host.cc b/firmware/src/Motherboard/Host.cc index d82038e..be8d16b 100644 --- a/firmware/src/Motherboard/Host.cc +++ b/firmware/src/Motherboard/Host.cc @@ -15,6 +15,7 @@ * along with this program. If not, see */ +#include "Configuration.hh" #include "Host.hh" #include "Command.hh" #include "Tool.hh" @@ -30,6 +31,8 @@ #include "Errors.hh" #include "Eeprom.hh" #include "EepromMap.hh" +#include "EepromDefaults.hh" +#include "StepperAccelPlanner.hh" namespace host { @@ -59,6 +62,18 @@ HostState currentState; bool do_host_reset = true; +void prepReset() { +#ifdef HAS_FILAMENT_COUNTER + command::addFilamentUsed(); +#endif + if (currentState == HOST_STATE_BUILDING + || currentState == HOST_STATE_BUILDING_FROM_SD + || currentState == HOST_STATE_ESTIMATING_FROM_SD) { + stopBuild(); + } + do_host_reset = true; // perform reset on next host slice +} + void runHostSlice() { InPacket& in = UART::getHostUART().in; OutPacket& out = UART::getHostUART().out; @@ -196,8 +211,12 @@ inline void handleGetBufferSize(const InPacket& from_host, OutPacket& to_host) { } inline void handleGetPosition(const InPacket& from_host, OutPacket& to_host) { +#ifdef HAS_STEPPER_ACCELERATION + steppers::drainAccelerationBuffer(); +#endif + ATOMIC_BLOCK(ATOMIC_FORCEON) { - const Point p = steppers::getPosition(); + const Point p = steppers::getCurrentPosition(); to_host.append8(RC_OK); to_host.append32(p[0]); to_host.append32(p[1]); @@ -216,8 +235,12 @@ inline void handleGetPosition(const InPacket& from_host, OutPacket& to_host) { } inline void handleGetPositionExt(const InPacket& from_host, OutPacket& to_host) { +#ifdef HAS_STEPPER_ACCELERATION + steppers::drainAccelerationBuffer(); +#endif + ATOMIC_BLOCK(ATOMIC_FORCEON) { - const Point p = steppers::getPosition(); + const Point p = steppers::getCurrentPosition(); to_host.append8(RC_OK); to_host.append32(p[0]); to_host.append32(p[1]); @@ -293,7 +316,7 @@ inline void handleNextFilename(const InPacket& from_host, OutPacket& to_host) { void doToolPause(OutPacket& to_host) { Timeout acquire_lock_timeout; - acquire_lock_timeout.start(HOST_TOOL_RESPONSE_TIMEOUT_MS); + acquire_lock_timeout.start(HOST_TOOL_RESPONSE_TIMEOUT_MICROS); while (!tool::getLock()) { if (acquire_lock_timeout.hasElapsed()) { to_host.append8(RC_DOWNSTREAM_TIMEOUT); @@ -333,7 +356,7 @@ inline void handleToolQuery(const InPacket& from_host, OutPacket& to_host) { return; } Timeout acquire_lock_timeout; - acquire_lock_timeout.start(HOST_TOOL_RESPONSE_TIMEOUT_MS); + acquire_lock_timeout.start(HOST_TOOL_RESPONSE_TIMEOUT_MICROS); while (!tool::getLock()) { if (acquire_lock_timeout.hasElapsed()) { to_host.append8(RC_DOWNSTREAM_TIMEOUT); @@ -374,7 +397,7 @@ inline void handlePause(const InPacket& from_host, OutPacket& to_host) { inline void handleIsFinished(const InPacket& from_host, OutPacket& to_host) { to_host.append8(RC_OK); ATOMIC_BLOCK(ATOMIC_FORCEON) { - bool done = !steppers::isRunning() && command::isEmpty(); + bool done = !steppers::isRunning() && command::isEmpty() && !blocks_queued(); to_host.append8(done?1:0); } } @@ -382,7 +405,7 @@ inline void handleIsFinished(const InPacket& from_host, OutPacket& to_host) { inline void handleReadEeprom(const InPacket& from_host, OutPacket& to_host) { uint16_t offset = from_host.read16(1); uint8_t length = from_host.read8(3); - uint8_t data[16]; + uint8_t data[32]; eeprom_read_block(data, (const void*) offset, length); to_host.append8(RC_OK); for (int i = 0; i < length; i++) { @@ -393,7 +416,7 @@ inline void handleReadEeprom(const InPacket& from_host, OutPacket& to_host) { inline void handleWriteEeprom(const InPacket& from_host, OutPacket& to_host) { uint16_t offset = from_host.read16(1); uint8_t length = from_host.read8(3); - uint8_t data[16]; + uint8_t data[32]; eeprom_read_block(data, (const void*) offset, length); for (int i = 0; i < length; i++) { data[i] = from_host.read8(i + 4); @@ -466,14 +489,7 @@ bool processQueryPacket(const InPacket& from_host, OutPacket& to_host) { case HOST_CMD_ABORT: // equivalent at current time case HOST_CMD_RESET: // TODO: This is fishy. - command::addFilamentUsed(); - if (currentState == HOST_STATE_BUILDING - || currentState == HOST_STATE_BUILDING_FROM_SD - || currentState == HOST_STATE_ESTIMATING_FROM_SD) { - stopBuild(); - } - - do_host_reset = true; // indicate reset after response has been sent + prepReset(); to_host.append8(RC_OK); return true; case HOST_CMD_GET_BUFFER_SIZE: @@ -537,7 +553,7 @@ char* getMachineName() { // If the machine name hasn't been loaded, load it if (machineName[0] == 0) { for(uint8_t i = 0; i < MAX_MACHINE_NAME_LEN; i++) { - machineName[i] = eeprom::getEeprom8(eeprom::MACHINE_NAME+i, 0); + machineName[i] = eeprom::getEeprom8(eeprom::MACHINE_NAME+i, EEPROM_DEFAULT_MACHINE_NAME); } } diff --git a/firmware/src/Motherboard/Host.hh b/firmware/src/Motherboard/Host.hh index 8bb3c9f..45b9704 100644 --- a/firmware/src/Motherboard/Host.hh +++ b/firmware/src/Motherboard/Host.hh @@ -72,6 +72,10 @@ bool isBuildComplete(); //Sets the host state to building (if it's estimating) void setHostStateBuildingFromSD(); + +// Prepares for a soft reset() by saving filament counters and stopping the build +void prepReset(); + } #endif // HOST_HH_ diff --git a/firmware/src/Motherboard/Main.cc b/firmware/src/Motherboard/Main.cc index 78e86bc..2f7805b 100644 --- a/firmware/src/Motherboard/Main.cc +++ b/firmware/src/Motherboard/Main.cc @@ -28,6 +28,7 @@ #include "SDCard.hh" #include "Eeprom.hh" #include "EepromMap.hh" +#include "EepromDefaults.hh" #include "Errors.hh" @@ -42,14 +43,18 @@ void reset(bool hard_reset) { ATOMIC_BLOCK(ATOMIC_FORCEON) { Motherboard& board = Motherboard::getBoard(); sdcard::reset(); - steppers::abort(); - steppers::reset(); command::reset(); +#ifndef ERASE_EEPROM_ON_EVERY_BOOT eeprom::init(); +#endif + sei(); board.reset(hard_reset); + steppers::abort(); + steppers::reset(); + #ifdef HAS_ESTOP - const uint8_t estop_conf = eeprom::getEeprom8(eeprom::ESTOP_CONFIGURATION, 0); + const uint8_t estop_conf = eeprom::getEeprom8(eeprom::ESTOP_CONFIGURATION, EEPROM_DEFAULT_ESTOP_CONFIGURATION); if (estop_conf == eeprom::ESTOP_CONF_ACTIVE_HIGH) { ESTOP_ENABLE_RISING_INT; } else if (estop_conf == eeprom::ESTOP_CONF_ACTIVE_LOW) { @@ -64,7 +69,6 @@ void reset(bool hard_reset) { atxLastPowerGood = ATX_POWER_GOOD.getValue(); #endif - sei(); // If we've just come from a hard reset, wait for 2.5 seconds before // trying to ping an extruder. This gives the extruder time to boot // before we send it a packet. @@ -83,6 +87,10 @@ void reset(bool hard_reset) { int main() { +#ifdef ERASE_EEPROM_ON_EVERY_BOOT + eeprom::erase(); +#endif + Motherboard& board = Motherboard::getBoard(); steppers::init(Motherboard::getBoard()); reset(true); @@ -96,6 +104,8 @@ int main() { command::runCommandSlice(); // Motherboard slice board.runMotherboardSlice(); + // Stepper slice + steppers::runSteppersSlice(); #ifdef HAS_ATX_POWER_GOOD /// Workaround for hardware issue, where powering on with USB connected @@ -117,14 +127,12 @@ int main() { #ifdef HAS_ESTOP ISR(ESTOP_vect, ISR_NOBLOCK) { // Emergency stop triggered; reset everything and kill the interface to RepG - INTERFACE_BAR_PIN.setValue(true); - INTERFACE_BAR_PIN.setDirection(true); tool::reset(); steppers::abort(); command::reset(); UART::getHostUART().enable(false); Motherboard::getBoard().indicateError(ERR_ESTOP); - Motherboard::getBoard().buzz(7, 10, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); + Motherboard::getBoard().buzz(7, 10, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, EEPROM_DEFAULT_BUZZER_REPEATS)); } #endif diff --git a/firmware/src/Motherboard/Point.hh b/firmware/src/Motherboard/Point.hh index f20a679..da7ea79 100644 --- a/firmware/src/Motherboard/Point.hh +++ b/firmware/src/Motherboard/Point.hh @@ -11,7 +11,7 @@ /// stepper axes present in the system. Can support 3 or 5 axes. class Point { private: - int32_t coordinates[AXIS_COUNT]; ///< n-dimensional coordinate + int32_t coordinates[5]; ///< n-dimensional coordinate public: /// Default point constructor Point(); diff --git a/firmware/src/Motherboard/SDCard.cc b/firmware/src/Motherboard/SDCard.cc index b6cfbd3..70b72da 100644 --- a/firmware/src/Motherboard/SDCard.cc +++ b/firmware/src/Motherboard/SDCard.cc @@ -241,6 +241,14 @@ void capturePacket(const Packet& packet) capturedBytes += packet.getLength(); } +#ifdef EEPROM_MENU_ENABLE + +/// Writes b to the open file +void writeByte(uint8_t b) { + fat_write_file(file, (uint8_t *)&b, (uintptr_t)1); +} + +#endif uint32_t finishCapture() { diff --git a/firmware/src/Motherboard/SDCard.hh b/firmware/src/Motherboard/SDCard.hh index 1316362..75eb96d 100644 --- a/firmware/src/Motherboard/SDCard.hh +++ b/firmware/src/Motherboard/SDCard.hh @@ -68,6 +68,10 @@ namespace sdcard { /// \param[in] packet Packet to write to file. void capturePacket(const Packet& packet); +#ifdef EEPROM_MENU_ENABLE + /// Writes b to the open file + void writeByte(uint8_t b); +#endif /// Complete the capture, and flush buffers. Return the number of bytes /// written to the card. diff --git a/firmware/src/Motherboard/SqrtTable.c b/firmware/src/Motherboard/SqrtTable.c new file mode 100644 index 0000000..543cec0 --- /dev/null +++ b/firmware/src/Motherboard/SqrtTable.c @@ -0,0 +1,77 @@ +// Generate the SqrtTable.hh file for a given "resolution" +// +// cc -o SqrtTable -I../shared/avrfix/ SqrtTable.c +// ./SqrtTable > SqrtTable.hh + +#include +#include + +#define TEST_ON_PC +#include "avrfix.h" + +#define SQRT_TABLE_SHIFT 6 +#define RESOLUTION (1 << SQRT_TABLE_SHIFT) + +main() +{ + _Accum as; + float fv, fs; + int i; + + printf( + "#ifndef __SQRT_TABLE_H__\n" + "\n" + "#include \"Configuration.hh\"\n" + "\n" + "#define __SQRT_TABLE_H__\n" + "\n" + "// Square root lookup table for finding the square root of 1 + x for 0 <= x <= 3\n" + "//\n" + "// sqrt(1.0 + x) = sqrt_table[(int)(x * SQRT_TABLE_RESOLUTION)]\n" + "// sqrt(1.0 + x) = sqrt_table[(int)(x << SQRT_TABLE_SHIFT)]\n" + "\n" + "#define SQRT_TABLE_SHIFT %d\n" + "#define SQRT_TABLE_RESOLUTION %d\n" + "\n" + "#ifdef FIXED\n" + "\n" + "#ifdef SMALL_4K_RAM\n" + " typedef _Accum PROGMEM prog_FPTYPE;\n" + " const static PROGMEM prog_FPTYPE sqrt_table[%d] = {\n" + "#else\n" + " static FPTYPE sqrt_table%d] = {\n" + "#endif\n", + SQRT_TABLE_SHIFT, RESOLUTION, 1 + RESOLUTION * 3); + + for (i = 0; i <= 3 * RESOLUTION; i++) + { + fv = (float)i / (float)RESOLUTION; + fs = sqrt(1.0 + fv); + as = ftok(fs); + + printf("\t0x%08x%c // ftok(sqrt(1.0 + (float)%d/%d.0)) = ftok(sqrt(1.0 + %f)) = ftok(%f)\n", + as, (i != 3*RESOLUTION) ? ',' : ' ', i, RESOLUTION, fv, fs); + } + + printf("};\n" + "\n" + "#else\n" + "\n" + "static float sqrt_table[%d] = {\n", + 1 + RESOLUTION * 3); + + for (i = 0; i <= 3 * RESOLUTION; i++) + { + fv = (float)i / (float)RESOLUTION; + fs = sqrt(1.0 + fv); + + printf("\t%25.23f%c // sqrt(1.0 + (float)%d/%d.0) = sqrt(1.0 + %f))\n", + fs, (i != 3*RESOLUTION) ? ',' : ' ', i, RESOLUTION, fv); + } + + printf("};\n" + "\n" + "#endif\n" + "\n" + "#endif\n"); +} diff --git a/firmware/src/Motherboard/SqrtTable.hh b/firmware/src/Motherboard/SqrtTable.hh new file mode 100644 index 0000000..a97cf89 --- /dev/null +++ b/firmware/src/Motherboard/SqrtTable.hh @@ -0,0 +1,418 @@ +#ifndef __SQRT_TABLE_H__ + +#include "Configuration.hh" + +#define __SQRT_TABLE_H__ + +// Square root lookup table for finding the square root of 1 + x for 0 <= x <= 3 +// +// sqrt(1.0 + x) = sqrt_table[(int)(x * SQRT_TABLE_RESOLUTION)] +// sqrt(1.0 + x) = sqrt_table[(int)(x << SQRT_TABLE_SHIFT)] + +#define SQRT_TABLE_SHIFT 6 +#define SQRT_TABLE_RESOLUTION 64 + +#ifdef FIXED + +#ifdef SMALL_4K_RAM + typedef _Accum PROGMEM prog_FPTYPE; + const static PROGMEM prog_FPTYPE sqrt_table[193] = { +#else + static FPTYPE sqrt_table[193] = { +#endif + 0x00010000, // ftok(sqrt(1.0 + (float)0/64.0)) = ftok(sqrt(1.0 + 0.000000)) = ftok(1.000000) + 0x000101fe, // ftok(sqrt(1.0 + (float)1/64.0)) = ftok(sqrt(1.0 + 0.015625)) = ftok(1.007782) + 0x000103f8, // ftok(sqrt(1.0 + (float)2/64.0)) = ftok(sqrt(1.0 + 0.031250)) = ftok(1.015505) + 0x000105ee, // ftok(sqrt(1.0 + (float)3/64.0)) = ftok(sqrt(1.0 + 0.046875)) = ftok(1.023169) + 0x000107e0, // ftok(sqrt(1.0 + (float)4/64.0)) = ftok(sqrt(1.0 + 0.062500)) = ftok(1.030776) + 0x000109cf, // ftok(sqrt(1.0 + (float)5/64.0)) = ftok(sqrt(1.0 + 0.078125)) = ftok(1.038328) + 0x00010bbb, // ftok(sqrt(1.0 + (float)6/64.0)) = ftok(sqrt(1.0 + 0.093750)) = ftok(1.045825) + 0x00010da3, // ftok(sqrt(1.0 + (float)7/64.0)) = ftok(sqrt(1.0 + 0.109375)) = ftok(1.053269) + 0x00010f87, // ftok(sqrt(1.0 + (float)8/64.0)) = ftok(sqrt(1.0 + 0.125000)) = ftok(1.060660) + 0x00011168, // ftok(sqrt(1.0 + (float)9/64.0)) = ftok(sqrt(1.0 + 0.140625)) = ftok(1.068000) + 0x00011346, // ftok(sqrt(1.0 + (float)10/64.0)) = ftok(sqrt(1.0 + 0.156250)) = ftok(1.075291) + 0x00011520, // ftok(sqrt(1.0 + (float)11/64.0)) = ftok(sqrt(1.0 + 0.171875)) = ftok(1.082532) + 0x000116f8, // ftok(sqrt(1.0 + (float)12/64.0)) = ftok(sqrt(1.0 + 0.187500)) = ftok(1.089725) + 0x000118cc, // ftok(sqrt(1.0 + (float)13/64.0)) = ftok(sqrt(1.0 + 0.203125)) = ftok(1.096871) + 0x00011a9d, // ftok(sqrt(1.0 + (float)14/64.0)) = ftok(sqrt(1.0 + 0.218750)) = ftok(1.103970) + 0x00011c6c, // ftok(sqrt(1.0 + (float)15/64.0)) = ftok(sqrt(1.0 + 0.234375)) = ftok(1.111024) + 0x00011e37, // ftok(sqrt(1.0 + (float)16/64.0)) = ftok(sqrt(1.0 + 0.250000)) = ftok(1.118034) + 0x00012000, // ftok(sqrt(1.0 + (float)17/64.0)) = ftok(sqrt(1.0 + 0.265625)) = ftok(1.125000) + 0x000121c5, // ftok(sqrt(1.0 + (float)18/64.0)) = ftok(sqrt(1.0 + 0.281250)) = ftok(1.131923) + 0x00012388, // ftok(sqrt(1.0 + (float)19/64.0)) = ftok(sqrt(1.0 + 0.296875)) = ftok(1.138804) + 0x00012548, // ftok(sqrt(1.0 + (float)20/64.0)) = ftok(sqrt(1.0 + 0.312500)) = ftok(1.145644) + 0x00012706, // ftok(sqrt(1.0 + (float)21/64.0)) = ftok(sqrt(1.0 + 0.328125)) = ftok(1.152443) + 0x000128c1, // ftok(sqrt(1.0 + (float)22/64.0)) = ftok(sqrt(1.0 + 0.343750)) = ftok(1.159202) + 0x00012a79, // ftok(sqrt(1.0 + (float)23/64.0)) = ftok(sqrt(1.0 + 0.359375)) = ftok(1.165922) + 0x00012c2f, // ftok(sqrt(1.0 + (float)24/64.0)) = ftok(sqrt(1.0 + 0.375000)) = ftok(1.172604) + 0x00012de3, // ftok(sqrt(1.0 + (float)25/64.0)) = ftok(sqrt(1.0 + 0.390625)) = ftok(1.179248) + 0x00012f94, // ftok(sqrt(1.0 + (float)26/64.0)) = ftok(sqrt(1.0 + 0.406250)) = ftok(1.185854) + 0x00013142, // ftok(sqrt(1.0 + (float)27/64.0)) = ftok(sqrt(1.0 + 0.421875)) = ftok(1.192424) + 0x000132ee, // ftok(sqrt(1.0 + (float)28/64.0)) = ftok(sqrt(1.0 + 0.437500)) = ftok(1.198958) + 0x00013498, // ftok(sqrt(1.0 + (float)29/64.0)) = ftok(sqrt(1.0 + 0.453125)) = ftok(1.205456) + 0x00013640, // ftok(sqrt(1.0 + (float)30/64.0)) = ftok(sqrt(1.0 + 0.468750)) = ftok(1.211920) + 0x000137e5, // ftok(sqrt(1.0 + (float)31/64.0)) = ftok(sqrt(1.0 + 0.484375)) = ftok(1.218349) + 0x00013988, // ftok(sqrt(1.0 + (float)32/64.0)) = ftok(sqrt(1.0 + 0.500000)) = ftok(1.224745) + 0x00013b29, // ftok(sqrt(1.0 + (float)33/64.0)) = ftok(sqrt(1.0 + 0.515625)) = ftok(1.231107) + 0x00013cc8, // ftok(sqrt(1.0 + (float)34/64.0)) = ftok(sqrt(1.0 + 0.531250)) = ftok(1.237437) + 0x00013e65, // ftok(sqrt(1.0 + (float)35/64.0)) = ftok(sqrt(1.0 + 0.546875)) = ftok(1.243734) + 0x00014000, // ftok(sqrt(1.0 + (float)36/64.0)) = ftok(sqrt(1.0 + 0.562500)) = ftok(1.250000) + 0x00014198, // ftok(sqrt(1.0 + (float)37/64.0)) = ftok(sqrt(1.0 + 0.578125)) = ftok(1.256234) + 0x0001432f, // ftok(sqrt(1.0 + (float)38/64.0)) = ftok(sqrt(1.0 + 0.593750)) = ftok(1.262438) + 0x000144c3, // ftok(sqrt(1.0 + (float)39/64.0)) = ftok(sqrt(1.0 + 0.609375)) = ftok(1.268611) + 0x00014656, // ftok(sqrt(1.0 + (float)40/64.0)) = ftok(sqrt(1.0 + 0.625000)) = ftok(1.274755) + 0x000147e7, // ftok(sqrt(1.0 + (float)41/64.0)) = ftok(sqrt(1.0 + 0.640625)) = ftok(1.280869) + 0x00014975, // ftok(sqrt(1.0 + (float)42/64.0)) = ftok(sqrt(1.0 + 0.656250)) = ftok(1.286954) + 0x00014b02, // ftok(sqrt(1.0 + (float)43/64.0)) = ftok(sqrt(1.0 + 0.671875)) = ftok(1.293010) + 0x00014c8d, // ftok(sqrt(1.0 + (float)44/64.0)) = ftok(sqrt(1.0 + 0.687500)) = ftok(1.299038) + 0x00014e16, // ftok(sqrt(1.0 + (float)45/64.0)) = ftok(sqrt(1.0 + 0.703125)) = ftok(1.305038) + 0x00014f9e, // ftok(sqrt(1.0 + (float)46/64.0)) = ftok(sqrt(1.0 + 0.718750)) = ftok(1.311011) + 0x00015124, // ftok(sqrt(1.0 + (float)47/64.0)) = ftok(sqrt(1.0 + 0.734375)) = ftok(1.316957) + 0x000152a7, // ftok(sqrt(1.0 + (float)48/64.0)) = ftok(sqrt(1.0 + 0.750000)) = ftok(1.322876) + 0x0001542a, // ftok(sqrt(1.0 + (float)49/64.0)) = ftok(sqrt(1.0 + 0.765625)) = ftok(1.328768) + 0x000155aa, // ftok(sqrt(1.0 + (float)50/64.0)) = ftok(sqrt(1.0 + 0.781250)) = ftok(1.334635) + 0x00015729, // ftok(sqrt(1.0 + (float)51/64.0)) = ftok(sqrt(1.0 + 0.796875)) = ftok(1.340476) + 0x000158a6, // ftok(sqrt(1.0 + (float)52/64.0)) = ftok(sqrt(1.0 + 0.812500)) = ftok(1.346291) + 0x00015a22, // ftok(sqrt(1.0 + (float)53/64.0)) = ftok(sqrt(1.0 + 0.828125)) = ftok(1.352082) + 0x00015b9b, // ftok(sqrt(1.0 + (float)54/64.0)) = ftok(sqrt(1.0 + 0.843750)) = ftok(1.357848) + 0x00015d14, // ftok(sqrt(1.0 + (float)55/64.0)) = ftok(sqrt(1.0 + 0.859375)) = ftok(1.363589) + 0x00015e8a, // ftok(sqrt(1.0 + (float)56/64.0)) = ftok(sqrt(1.0 + 0.875000)) = ftok(1.369306) + 0x00016000, // ftok(sqrt(1.0 + (float)57/64.0)) = ftok(sqrt(1.0 + 0.890625)) = ftok(1.375000) + 0x00016173, // ftok(sqrt(1.0 + (float)58/64.0)) = ftok(sqrt(1.0 + 0.906250)) = ftok(1.380670) + 0x000162e5, // ftok(sqrt(1.0 + (float)59/64.0)) = ftok(sqrt(1.0 + 0.921875)) = ftok(1.386317) + 0x00016456, // ftok(sqrt(1.0 + (float)60/64.0)) = ftok(sqrt(1.0 + 0.937500)) = ftok(1.391941) + 0x000165c5, // ftok(sqrt(1.0 + (float)61/64.0)) = ftok(sqrt(1.0 + 0.953125)) = ftok(1.397542) + 0x00016732, // ftok(sqrt(1.0 + (float)62/64.0)) = ftok(sqrt(1.0 + 0.968750)) = ftok(1.403121) + 0x0001689f, // ftok(sqrt(1.0 + (float)63/64.0)) = ftok(sqrt(1.0 + 0.984375)) = ftok(1.408678) + 0x00016a09, // ftok(sqrt(1.0 + (float)64/64.0)) = ftok(sqrt(1.0 + 1.000000)) = ftok(1.414214) + 0x00016b73, // ftok(sqrt(1.0 + (float)65/64.0)) = ftok(sqrt(1.0 + 1.015625)) = ftok(1.419727) + 0x00016cdb, // ftok(sqrt(1.0 + (float)66/64.0)) = ftok(sqrt(1.0 + 1.031250)) = ftok(1.425219) + 0x00016e41, // ftok(sqrt(1.0 + (float)67/64.0)) = ftok(sqrt(1.0 + 1.046875)) = ftok(1.430690) + 0x00016fa6, // ftok(sqrt(1.0 + (float)68/64.0)) = ftok(sqrt(1.0 + 1.062500)) = ftok(1.436141) + 0x0001710a, // ftok(sqrt(1.0 + (float)69/64.0)) = ftok(sqrt(1.0 + 1.078125)) = ftok(1.441570) + 0x0001726d, // ftok(sqrt(1.0 + (float)70/64.0)) = ftok(sqrt(1.0 + 1.093750)) = ftok(1.446980) + 0x000173ce, // ftok(sqrt(1.0 + (float)71/64.0)) = ftok(sqrt(1.0 + 1.109375)) = ftok(1.452369) + 0x0001752e, // ftok(sqrt(1.0 + (float)72/64.0)) = ftok(sqrt(1.0 + 1.125000)) = ftok(1.457738) + 0x0001768c, // ftok(sqrt(1.0 + (float)73/64.0)) = ftok(sqrt(1.0 + 1.140625)) = ftok(1.463087) + 0x000177ea, // ftok(sqrt(1.0 + (float)74/64.0)) = ftok(sqrt(1.0 + 1.156250)) = ftok(1.468418) + 0x00017946, // ftok(sqrt(1.0 + (float)75/64.0)) = ftok(sqrt(1.0 + 1.171875)) = ftok(1.473728) + 0x00017aa1, // ftok(sqrt(1.0 + (float)76/64.0)) = ftok(sqrt(1.0 + 1.187500)) = ftok(1.479020) + 0x00017bfa, // ftok(sqrt(1.0 + (float)77/64.0)) = ftok(sqrt(1.0 + 1.203125)) = ftok(1.484293) + 0x00017d52, // ftok(sqrt(1.0 + (float)78/64.0)) = ftok(sqrt(1.0 + 1.218750)) = ftok(1.489547) + 0x00017eaa, // ftok(sqrt(1.0 + (float)79/64.0)) = ftok(sqrt(1.0 + 1.234375)) = ftok(1.494783) + 0x00018000, // ftok(sqrt(1.0 + (float)80/64.0)) = ftok(sqrt(1.0 + 1.250000)) = ftok(1.500000) + 0x00018154, // ftok(sqrt(1.0 + (float)81/64.0)) = ftok(sqrt(1.0 + 1.265625)) = ftok(1.505199) + 0x000182a8, // ftok(sqrt(1.0 + (float)82/64.0)) = ftok(sqrt(1.0 + 1.281250)) = ftok(1.510381) + 0x000183fa, // ftok(sqrt(1.0 + (float)83/64.0)) = ftok(sqrt(1.0 + 1.296875)) = ftok(1.515544) + 0x0001854b, // ftok(sqrt(1.0 + (float)84/64.0)) = ftok(sqrt(1.0 + 1.312500)) = ftok(1.520691) + 0x0001869c, // ftok(sqrt(1.0 + (float)85/64.0)) = ftok(sqrt(1.0 + 1.328125)) = ftok(1.525819) + 0x000187eb, // ftok(sqrt(1.0 + (float)86/64.0)) = ftok(sqrt(1.0 + 1.343750)) = ftok(1.530931) + 0x00018938, // ftok(sqrt(1.0 + (float)87/64.0)) = ftok(sqrt(1.0 + 1.359375)) = ftok(1.536026) + 0x00018a85, // ftok(sqrt(1.0 + (float)88/64.0)) = ftok(sqrt(1.0 + 1.375000)) = ftok(1.541103) + 0x00018bd1, // ftok(sqrt(1.0 + (float)89/64.0)) = ftok(sqrt(1.0 + 1.390625)) = ftok(1.546165) + 0x00018d1c, // ftok(sqrt(1.0 + (float)90/64.0)) = ftok(sqrt(1.0 + 1.406250)) = ftok(1.551209) + 0x00018e65, // ftok(sqrt(1.0 + (float)91/64.0)) = ftok(sqrt(1.0 + 1.421875)) = ftok(1.556237) + 0x00018fae, // ftok(sqrt(1.0 + (float)92/64.0)) = ftok(sqrt(1.0 + 1.437500)) = ftok(1.561249) + 0x000190f5, // ftok(sqrt(1.0 + (float)93/64.0)) = ftok(sqrt(1.0 + 1.453125)) = ftok(1.566246) + 0x0001923b, // ftok(sqrt(1.0 + (float)94/64.0)) = ftok(sqrt(1.0 + 1.468750)) = ftok(1.571226) + 0x00019381, // ftok(sqrt(1.0 + (float)95/64.0)) = ftok(sqrt(1.0 + 1.484375)) = ftok(1.576190) + 0x000194c5, // ftok(sqrt(1.0 + (float)96/64.0)) = ftok(sqrt(1.0 + 1.500000)) = ftok(1.581139) + 0x00019608, // ftok(sqrt(1.0 + (float)97/64.0)) = ftok(sqrt(1.0 + 1.515625)) = ftok(1.586072) + 0x0001974b, // ftok(sqrt(1.0 + (float)98/64.0)) = ftok(sqrt(1.0 + 1.531250)) = ftok(1.590990) + 0x0001988c, // ftok(sqrt(1.0 + (float)99/64.0)) = ftok(sqrt(1.0 + 1.546875)) = ftok(1.595893) + 0x000199cc, // ftok(sqrt(1.0 + (float)100/64.0)) = ftok(sqrt(1.0 + 1.562500)) = ftok(1.600781) + 0x00019b0c, // ftok(sqrt(1.0 + (float)101/64.0)) = ftok(sqrt(1.0 + 1.578125)) = ftok(1.605654) + 0x00019c4a, // ftok(sqrt(1.0 + (float)102/64.0)) = ftok(sqrt(1.0 + 1.593750)) = ftok(1.610512) + 0x00019d87, // ftok(sqrt(1.0 + (float)103/64.0)) = ftok(sqrt(1.0 + 1.609375)) = ftok(1.615356) + 0x00019ec4, // ftok(sqrt(1.0 + (float)104/64.0)) = ftok(sqrt(1.0 + 1.625000)) = ftok(1.620185) + 0x0001a000, // ftok(sqrt(1.0 + (float)105/64.0)) = ftok(sqrt(1.0 + 1.640625)) = ftok(1.625000) + 0x0001a13a, // ftok(sqrt(1.0 + (float)106/64.0)) = ftok(sqrt(1.0 + 1.656250)) = ftok(1.629801) + 0x0001a274, // ftok(sqrt(1.0 + (float)107/64.0)) = ftok(sqrt(1.0 + 1.671875)) = ftok(1.634587) + 0x0001a3ad, // ftok(sqrt(1.0 + (float)108/64.0)) = ftok(sqrt(1.0 + 1.687500)) = ftok(1.639360) + 0x0001a4e4, // ftok(sqrt(1.0 + (float)109/64.0)) = ftok(sqrt(1.0 + 1.703125)) = ftok(1.644118) + 0x0001a61b, // ftok(sqrt(1.0 + (float)110/64.0)) = ftok(sqrt(1.0 + 1.718750)) = ftok(1.648863) + 0x0001a751, // ftok(sqrt(1.0 + (float)111/64.0)) = ftok(sqrt(1.0 + 1.734375)) = ftok(1.653595) + 0x0001a887, // ftok(sqrt(1.0 + (float)112/64.0)) = ftok(sqrt(1.0 + 1.750000)) = ftok(1.658312) + 0x0001a9bb, // ftok(sqrt(1.0 + (float)113/64.0)) = ftok(sqrt(1.0 + 1.765625)) = ftok(1.663017) + 0x0001aaee, // ftok(sqrt(1.0 + (float)114/64.0)) = ftok(sqrt(1.0 + 1.781250)) = ftok(1.667708) + 0x0001ac21, // ftok(sqrt(1.0 + (float)115/64.0)) = ftok(sqrt(1.0 + 1.796875)) = ftok(1.672386) + 0x0001ad53, // ftok(sqrt(1.0 + (float)116/64.0)) = ftok(sqrt(1.0 + 1.812500)) = ftok(1.677051) + 0x0001ae84, // ftok(sqrt(1.0 + (float)117/64.0)) = ftok(sqrt(1.0 + 1.828125)) = ftok(1.681703) + 0x0001afb4, // ftok(sqrt(1.0 + (float)118/64.0)) = ftok(sqrt(1.0 + 1.843750)) = ftok(1.686342) + 0x0001b0e3, // ftok(sqrt(1.0 + (float)119/64.0)) = ftok(sqrt(1.0 + 1.859375)) = ftok(1.690969) + 0x0001b211, // ftok(sqrt(1.0 + (float)120/64.0)) = ftok(sqrt(1.0 + 1.875000)) = ftok(1.695583) + 0x0001b33f, // ftok(sqrt(1.0 + (float)121/64.0)) = ftok(sqrt(1.0 + 1.890625)) = ftok(1.700184) + 0x0001b46b, // ftok(sqrt(1.0 + (float)122/64.0)) = ftok(sqrt(1.0 + 1.906250)) = ftok(1.704773) + 0x0001b597, // ftok(sqrt(1.0 + (float)123/64.0)) = ftok(sqrt(1.0 + 1.921875)) = ftok(1.709349) + 0x0001b6c3, // ftok(sqrt(1.0 + (float)124/64.0)) = ftok(sqrt(1.0 + 1.937500)) = ftok(1.713914) + 0x0001b7ed, // ftok(sqrt(1.0 + (float)125/64.0)) = ftok(sqrt(1.0 + 1.953125)) = ftok(1.718466) + 0x0001b916, // ftok(sqrt(1.0 + (float)126/64.0)) = ftok(sqrt(1.0 + 1.968750)) = ftok(1.723006) + 0x0001ba3f, // ftok(sqrt(1.0 + (float)127/64.0)) = ftok(sqrt(1.0 + 1.984375)) = ftok(1.727534) + 0x0001bb67, // ftok(sqrt(1.0 + (float)128/64.0)) = ftok(sqrt(1.0 + 2.000000)) = ftok(1.732051) + 0x0001bc8e, // ftok(sqrt(1.0 + (float)129/64.0)) = ftok(sqrt(1.0 + 2.015625)) = ftok(1.736555) + 0x0001bdb5, // ftok(sqrt(1.0 + (float)130/64.0)) = ftok(sqrt(1.0 + 2.031250)) = ftok(1.741049) + 0x0001bedb, // ftok(sqrt(1.0 + (float)131/64.0)) = ftok(sqrt(1.0 + 2.046875)) = ftok(1.745530) + 0x0001c000, // ftok(sqrt(1.0 + (float)132/64.0)) = ftok(sqrt(1.0 + 2.062500)) = ftok(1.750000) + 0x0001c124, // ftok(sqrt(1.0 + (float)133/64.0)) = ftok(sqrt(1.0 + 2.078125)) = ftok(1.754459) + 0x0001c247, // ftok(sqrt(1.0 + (float)134/64.0)) = ftok(sqrt(1.0 + 2.093750)) = ftok(1.758906) + 0x0001c36a, // ftok(sqrt(1.0 + (float)135/64.0)) = ftok(sqrt(1.0 + 2.109375)) = ftok(1.763342) + 0x0001c48c, // ftok(sqrt(1.0 + (float)136/64.0)) = ftok(sqrt(1.0 + 2.125000)) = ftok(1.767767) + 0x0001c5ad, // ftok(sqrt(1.0 + (float)137/64.0)) = ftok(sqrt(1.0 + 2.140625)) = ftok(1.772181) + 0x0001c6ce, // ftok(sqrt(1.0 + (float)138/64.0)) = ftok(sqrt(1.0 + 2.156250)) = ftok(1.776584) + 0x0001c7ee, // ftok(sqrt(1.0 + (float)139/64.0)) = ftok(sqrt(1.0 + 2.171875)) = ftok(1.780976) + 0x0001c90d, // ftok(sqrt(1.0 + (float)140/64.0)) = ftok(sqrt(1.0 + 2.187500)) = ftok(1.785357) + 0x0001ca2b, // ftok(sqrt(1.0 + (float)141/64.0)) = ftok(sqrt(1.0 + 2.203125)) = ftok(1.789728) + 0x0001cb49, // ftok(sqrt(1.0 + (float)142/64.0)) = ftok(sqrt(1.0 + 2.218750)) = ftok(1.794088) + 0x0001cc66, // ftok(sqrt(1.0 + (float)143/64.0)) = ftok(sqrt(1.0 + 2.234375)) = ftok(1.798437) + 0x0001cd82, // ftok(sqrt(1.0 + (float)144/64.0)) = ftok(sqrt(1.0 + 2.250000)) = ftok(1.802776) + 0x0001ce9e, // ftok(sqrt(1.0 + (float)145/64.0)) = ftok(sqrt(1.0 + 2.265625)) = ftok(1.807104) + 0x0001cfb9, // ftok(sqrt(1.0 + (float)146/64.0)) = ftok(sqrt(1.0 + 2.281250)) = ftok(1.811422) + 0x0001d0d3, // ftok(sqrt(1.0 + (float)147/64.0)) = ftok(sqrt(1.0 + 2.296875)) = ftok(1.815730) + 0x0001d1ed, // ftok(sqrt(1.0 + (float)148/64.0)) = ftok(sqrt(1.0 + 2.312500)) = ftok(1.820027) + 0x0001d306, // ftok(sqrt(1.0 + (float)149/64.0)) = ftok(sqrt(1.0 + 2.328125)) = ftok(1.824315) + 0x0001d41e, // ftok(sqrt(1.0 + (float)150/64.0)) = ftok(sqrt(1.0 + 2.343750)) = ftok(1.828592) + 0x0001d536, // ftok(sqrt(1.0 + (float)151/64.0)) = ftok(sqrt(1.0 + 2.359375)) = ftok(1.832860) + 0x0001d64d, // ftok(sqrt(1.0 + (float)152/64.0)) = ftok(sqrt(1.0 + 2.375000)) = ftok(1.837117) + 0x0001d763, // ftok(sqrt(1.0 + (float)153/64.0)) = ftok(sqrt(1.0 + 2.390625)) = ftok(1.841365) + 0x0001d879, // ftok(sqrt(1.0 + (float)154/64.0)) = ftok(sqrt(1.0 + 2.406250)) = ftok(1.845603) + 0x0001d98e, // ftok(sqrt(1.0 + (float)155/64.0)) = ftok(sqrt(1.0 + 2.421875)) = ftok(1.849831) + 0x0001daa2, // ftok(sqrt(1.0 + (float)156/64.0)) = ftok(sqrt(1.0 + 2.437500)) = ftok(1.854050) + 0x0001dbb6, // ftok(sqrt(1.0 + (float)157/64.0)) = ftok(sqrt(1.0 + 2.453125)) = ftok(1.858259) + 0x0001dcca, // ftok(sqrt(1.0 + (float)158/64.0)) = ftok(sqrt(1.0 + 2.468750)) = ftok(1.862458) + 0x0001dddc, // ftok(sqrt(1.0 + (float)159/64.0)) = ftok(sqrt(1.0 + 2.484375)) = ftok(1.866648) + 0x0001deee, // ftok(sqrt(1.0 + (float)160/64.0)) = ftok(sqrt(1.0 + 2.500000)) = ftok(1.870829) + 0x0001e000, // ftok(sqrt(1.0 + (float)161/64.0)) = ftok(sqrt(1.0 + 2.515625)) = ftok(1.875000) + 0x0001e110, // ftok(sqrt(1.0 + (float)162/64.0)) = ftok(sqrt(1.0 + 2.531250)) = ftok(1.879162) + 0x0001e220, // ftok(sqrt(1.0 + (float)163/64.0)) = ftok(sqrt(1.0 + 2.546875)) = ftok(1.883315) + 0x0001e330, // ftok(sqrt(1.0 + (float)164/64.0)) = ftok(sqrt(1.0 + 2.562500)) = ftok(1.887459) + 0x0001e43f, // ftok(sqrt(1.0 + (float)165/64.0)) = ftok(sqrt(1.0 + 2.578125)) = ftok(1.891593) + 0x0001e54d, // ftok(sqrt(1.0 + (float)166/64.0)) = ftok(sqrt(1.0 + 2.593750)) = ftok(1.895719) + 0x0001e65b, // ftok(sqrt(1.0 + (float)167/64.0)) = ftok(sqrt(1.0 + 2.609375)) = ftok(1.899835) + 0x0001e768, // ftok(sqrt(1.0 + (float)168/64.0)) = ftok(sqrt(1.0 + 2.625000)) = ftok(1.903943) + 0x0001e875, // ftok(sqrt(1.0 + (float)169/64.0)) = ftok(sqrt(1.0 + 2.640625)) = ftok(1.908042) + 0x0001e981, // ftok(sqrt(1.0 + (float)170/64.0)) = ftok(sqrt(1.0 + 2.656250)) = ftok(1.912132) + 0x0001ea8c, // ftok(sqrt(1.0 + (float)171/64.0)) = ftok(sqrt(1.0 + 2.671875)) = ftok(1.916214) + 0x0001eb97, // ftok(sqrt(1.0 + (float)172/64.0)) = ftok(sqrt(1.0 + 2.687500)) = ftok(1.920286) + 0x0001eca2, // ftok(sqrt(1.0 + (float)173/64.0)) = ftok(sqrt(1.0 + 2.703125)) = ftok(1.924351) + 0x0001edac, // ftok(sqrt(1.0 + (float)174/64.0)) = ftok(sqrt(1.0 + 2.718750)) = ftok(1.928406) + 0x0001eeb5, // ftok(sqrt(1.0 + (float)175/64.0)) = ftok(sqrt(1.0 + 2.734375)) = ftok(1.932453) + 0x0001efbd, // ftok(sqrt(1.0 + (float)176/64.0)) = ftok(sqrt(1.0 + 2.750000)) = ftok(1.936492) + 0x0001f0c6, // ftok(sqrt(1.0 + (float)177/64.0)) = ftok(sqrt(1.0 + 2.765625)) = ftok(1.940522) + 0x0001f1cd, // ftok(sqrt(1.0 + (float)178/64.0)) = ftok(sqrt(1.0 + 2.781250)) = ftok(1.944544) + 0x0001f2d4, // ftok(sqrt(1.0 + (float)179/64.0)) = ftok(sqrt(1.0 + 2.796875)) = ftok(1.948557) + 0x0001f3db, // ftok(sqrt(1.0 + (float)180/64.0)) = ftok(sqrt(1.0 + 2.812500)) = ftok(1.952562) + 0x0001f4e1, // ftok(sqrt(1.0 + (float)181/64.0)) = ftok(sqrt(1.0 + 2.828125)) = ftok(1.956560) + 0x0001f5e6, // ftok(sqrt(1.0 + (float)182/64.0)) = ftok(sqrt(1.0 + 2.843750)) = ftok(1.960548) + 0x0001f6eb, // ftok(sqrt(1.0 + (float)183/64.0)) = ftok(sqrt(1.0 + 2.859375)) = ftok(1.964529) + 0x0001f7ef, // ftok(sqrt(1.0 + (float)184/64.0)) = ftok(sqrt(1.0 + 2.875000)) = ftok(1.968502) + 0x0001f8f3, // ftok(sqrt(1.0 + (float)185/64.0)) = ftok(sqrt(1.0 + 2.890625)) = ftok(1.972467) + 0x0001f9f6, // ftok(sqrt(1.0 + (float)186/64.0)) = ftok(sqrt(1.0 + 2.906250)) = ftok(1.976424) + 0x0001faf9, // ftok(sqrt(1.0 + (float)187/64.0)) = ftok(sqrt(1.0 + 2.921875)) = ftok(1.980372) + 0x0001fbfb, // ftok(sqrt(1.0 + (float)188/64.0)) = ftok(sqrt(1.0 + 2.937500)) = ftok(1.984313) + 0x0001fcfd, // ftok(sqrt(1.0 + (float)189/64.0)) = ftok(sqrt(1.0 + 2.953125)) = ftok(1.988247) + 0x0001fdfe, // ftok(sqrt(1.0 + (float)190/64.0)) = ftok(sqrt(1.0 + 2.968750)) = ftok(1.992172) + 0x0001feff, // ftok(sqrt(1.0 + (float)191/64.0)) = ftok(sqrt(1.0 + 2.984375)) = ftok(1.996090) + 0x00020000 // ftok(sqrt(1.0 + (float)192/64.0)) = ftok(sqrt(1.0 + 3.000000)) = ftok(2.000000) +}; + +#else + +static float sqrt_table[193] = { + 1.00000000000000000000000, // sqrt(1.0 + (float)0/64.0) = sqrt(1.0 + 0.000000)) + 1.00778222084045410156250, // sqrt(1.0 + (float)1/64.0) = sqrt(1.0 + 0.015625)) + 1.01550483703613281250000, // sqrt(1.0 + (float)2/64.0) = sqrt(1.0 + 0.031250)) + 1.02316904067993164062500, // sqrt(1.0 + (float)3/64.0) = sqrt(1.0 + 0.046875)) + 1.03077638149261474609375, // sqrt(1.0 + (float)4/64.0) = sqrt(1.0 + 0.062500)) + 1.03832793235778808593750, // sqrt(1.0 + (float)5/64.0) = sqrt(1.0 + 0.078125)) + 1.04582500457763671875000, // sqrt(1.0 + (float)6/64.0) = sqrt(1.0 + 0.093750)) + 1.05326867103576660156250, // sqrt(1.0 + (float)7/64.0) = sqrt(1.0 + 0.109375)) + 1.06066012382507324218750, // sqrt(1.0 + (float)8/64.0) = sqrt(1.0 + 0.125000)) + 1.06800043582916259765625, // sqrt(1.0 + (float)9/64.0) = sqrt(1.0 + 0.140625)) + 1.07529067993164062500000, // sqrt(1.0 + (float)10/64.0) = sqrt(1.0 + 0.156250)) + 1.08253180980682373046875, // sqrt(1.0 + (float)11/64.0) = sqrt(1.0 + 0.171875)) + 1.08972477912902832031250, // sqrt(1.0 + (float)12/64.0) = sqrt(1.0 + 0.187500)) + 1.09687054157257080078125, // sqrt(1.0 + (float)13/64.0) = sqrt(1.0 + 0.203125)) + 1.10397005081176757812500, // sqrt(1.0 + (float)14/64.0) = sqrt(1.0 + 0.218750)) + 1.11102426052093505859375, // sqrt(1.0 + (float)15/64.0) = sqrt(1.0 + 0.234375)) + 1.11803400516510009765625, // sqrt(1.0 + (float)16/64.0) = sqrt(1.0 + 0.250000)) + 1.12500000000000000000000, // sqrt(1.0 + (float)17/64.0) = sqrt(1.0 + 0.265625)) + 1.13192319869995117187500, // sqrt(1.0 + (float)18/64.0) = sqrt(1.0 + 0.281250)) + 1.13880419731140136718750, // sqrt(1.0 + (float)19/64.0) = sqrt(1.0 + 0.296875)) + 1.14564394950866699218750, // sqrt(1.0 + (float)20/64.0) = sqrt(1.0 + 0.312500)) + 1.15244305133819580078125, // sqrt(1.0 + (float)21/64.0) = sqrt(1.0 + 0.328125)) + 1.15920233726501464843750, // sqrt(1.0 + (float)22/64.0) = sqrt(1.0 + 0.343750)) + 1.16592240333557128906250, // sqrt(1.0 + (float)23/64.0) = sqrt(1.0 + 0.359375)) + 1.17260396480560302734375, // sqrt(1.0 + (float)24/64.0) = sqrt(1.0 + 0.375000)) + 1.17924761772155761718750, // sqrt(1.0 + (float)25/64.0) = sqrt(1.0 + 0.390625)) + 1.18585407733917236328125, // sqrt(1.0 + (float)26/64.0) = sqrt(1.0 + 0.406250)) + 1.19242405891418457031250, // sqrt(1.0 + (float)27/64.0) = sqrt(1.0 + 0.421875)) + 1.19895792007446289062500, // sqrt(1.0 + (float)28/64.0) = sqrt(1.0 + 0.437500)) + 1.20545637607574462890625, // sqrt(1.0 + (float)29/64.0) = sqrt(1.0 + 0.453125)) + 1.21192002296447753906250, // sqrt(1.0 + (float)30/64.0) = sqrt(1.0 + 0.468750)) + 1.21834933757781982421875, // sqrt(1.0 + (float)31/64.0) = sqrt(1.0 + 0.484375)) + 1.22474491596221923828125, // sqrt(1.0 + (float)32/64.0) = sqrt(1.0 + 0.500000)) + 1.23110723495483398437500, // sqrt(1.0 + (float)33/64.0) = sqrt(1.0 + 0.515625)) + 1.23743689060211181640625, // sqrt(1.0 + (float)34/64.0) = sqrt(1.0 + 0.531250)) + 1.24373424053192138671875, // sqrt(1.0 + (float)35/64.0) = sqrt(1.0 + 0.546875)) + 1.25000000000000000000000, // sqrt(1.0 + (float)36/64.0) = sqrt(1.0 + 0.562500)) + 1.25623440742492675781250, // sqrt(1.0 + (float)37/64.0) = sqrt(1.0 + 0.578125)) + 1.26243805885314941406250, // sqrt(1.0 + (float)38/64.0) = sqrt(1.0 + 0.593750)) + 1.26861143112182617187500, // sqrt(1.0 + (float)39/64.0) = sqrt(1.0 + 0.609375)) + 1.27475488185882568359375, // sqrt(1.0 + (float)40/64.0) = sqrt(1.0 + 0.625000)) + 1.28086888790130615234375, // sqrt(1.0 + (float)41/64.0) = sqrt(1.0 + 0.640625)) + 1.28695380687713623046875, // sqrt(1.0 + (float)42/64.0) = sqrt(1.0 + 0.656250)) + 1.29300999641418457031250, // sqrt(1.0 + (float)43/64.0) = sqrt(1.0 + 0.671875)) + 1.29903805255889892578125, // sqrt(1.0 + (float)44/64.0) = sqrt(1.0 + 0.687500)) + 1.30503833293914794921875, // sqrt(1.0 + (float)45/64.0) = sqrt(1.0 + 0.703125)) + 1.31101107597351074218750, // sqrt(1.0 + (float)46/64.0) = sqrt(1.0 + 0.718750)) + 1.31695675849914550781250, // sqrt(1.0 + (float)47/64.0) = sqrt(1.0 + 0.734375)) + 1.32287561893463134765625, // sqrt(1.0 + (float)48/64.0) = sqrt(1.0 + 0.750000)) + 1.32876825332641601562500, // sqrt(1.0 + (float)49/64.0) = sqrt(1.0 + 0.765625)) + 1.33463478088378906250000, // sqrt(1.0 + (float)50/64.0) = sqrt(1.0 + 0.781250)) + 1.34047567844390869140625, // sqrt(1.0 + (float)51/64.0) = sqrt(1.0 + 0.796875)) + 1.34629118442535400390625, // sqrt(1.0 + (float)52/64.0) = sqrt(1.0 + 0.812500)) + 1.35208177566528320312500, // sqrt(1.0 + (float)53/64.0) = sqrt(1.0 + 0.828125)) + 1.35784757137298583984375, // sqrt(1.0 + (float)54/64.0) = sqrt(1.0 + 0.843750)) + 1.36358904838562011718750, // sqrt(1.0 + (float)55/64.0) = sqrt(1.0 + 0.859375)) + 1.36930644512176513671875, // sqrt(1.0 + (float)56/64.0) = sqrt(1.0 + 0.875000)) + 1.37500000000000000000000, // sqrt(1.0 + (float)57/64.0) = sqrt(1.0 + 0.890625)) + 1.38067007064819335937500, // sqrt(1.0 + (float)58/64.0) = sqrt(1.0 + 0.906250)) + 1.38631701469421386718750, // sqrt(1.0 + (float)59/64.0) = sqrt(1.0 + 0.921875)) + 1.39194107055664062500000, // sqrt(1.0 + (float)60/64.0) = sqrt(1.0 + 0.937500)) + 1.39754247665405273437500, // sqrt(1.0 + (float)61/64.0) = sqrt(1.0 + 0.953125)) + 1.40312147140502929687500, // sqrt(1.0 + (float)62/64.0) = sqrt(1.0 + 0.968750)) + 1.40867841243743896484375, // sqrt(1.0 + (float)63/64.0) = sqrt(1.0 + 0.984375)) + 1.41421353816986083984375, // sqrt(1.0 + (float)64/64.0) = sqrt(1.0 + 1.000000)) + 1.41972708702087402343750, // sqrt(1.0 + (float)65/64.0) = sqrt(1.0 + 1.015625)) + 1.42521929740905761718750, // sqrt(1.0 + (float)66/64.0) = sqrt(1.0 + 1.031250)) + 1.43069040775299072265625, // sqrt(1.0 + (float)67/64.0) = sqrt(1.0 + 1.046875)) + 1.43614065647125244140625, // sqrt(1.0 + (float)68/64.0) = sqrt(1.0 + 1.062500)) + 1.44157028198242187500000, // sqrt(1.0 + (float)69/64.0) = sqrt(1.0 + 1.078125)) + 1.44697964191436767578125, // sqrt(1.0 + (float)70/64.0) = sqrt(1.0 + 1.093750)) + 1.45236873626708984375000, // sqrt(1.0 + (float)71/64.0) = sqrt(1.0 + 1.109375)) + 1.45773792266845703125000, // sqrt(1.0 + (float)72/64.0) = sqrt(1.0 + 1.125000)) + 1.46308743953704833984375, // sqrt(1.0 + (float)73/64.0) = sqrt(1.0 + 1.140625)) + 1.46841752529144287109375, // sqrt(1.0 + (float)74/64.0) = sqrt(1.0 + 1.156250)) + 1.47372829914093017578125, // sqrt(1.0 + (float)75/64.0) = sqrt(1.0 + 1.171875)) + 1.47901999950408935546875, // sqrt(1.0 + (float)76/64.0) = sqrt(1.0 + 1.187500)) + 1.48429274559020996093750, // sqrt(1.0 + (float)77/64.0) = sqrt(1.0 + 1.203125)) + 1.48954689502716064453125, // sqrt(1.0 + (float)78/64.0) = sqrt(1.0 + 1.218750)) + 1.49478256702423095703125, // sqrt(1.0 + (float)79/64.0) = sqrt(1.0 + 1.234375)) + 1.50000000000000000000000, // sqrt(1.0 + (float)80/64.0) = sqrt(1.0 + 1.250000)) + 1.50519931316375732421875, // sqrt(1.0 + (float)81/64.0) = sqrt(1.0 + 1.265625)) + 1.51038074493408203125000, // sqrt(1.0 + (float)82/64.0) = sqrt(1.0 + 1.281250)) + 1.51554441452026367187500, // sqrt(1.0 + (float)83/64.0) = sqrt(1.0 + 1.296875)) + 1.52069067955017089843750, // sqrt(1.0 + (float)84/64.0) = sqrt(1.0 + 1.312500)) + 1.52581942081451416015625, // sqrt(1.0 + (float)85/64.0) = sqrt(1.0 + 1.328125)) + 1.53093111515045166015625, // sqrt(1.0 + (float)86/64.0) = sqrt(1.0 + 1.343750)) + 1.53602576255798339843750, // sqrt(1.0 + (float)87/64.0) = sqrt(1.0 + 1.359375)) + 1.54110348224639892578125, // sqrt(1.0 + (float)88/64.0) = sqrt(1.0 + 1.375000)) + 1.54616463184356689453125, // sqrt(1.0 + (float)89/64.0) = sqrt(1.0 + 1.390625)) + 1.55120921134948730468750, // sqrt(1.0 + (float)90/64.0) = sqrt(1.0 + 1.406250)) + 1.55623745918273925781250, // sqrt(1.0 + (float)91/64.0) = sqrt(1.0 + 1.421875)) + 1.56124949455261230468750, // sqrt(1.0 + (float)92/64.0) = sqrt(1.0 + 1.437500)) + 1.56624555587768554687500, // sqrt(1.0 + (float)93/64.0) = sqrt(1.0 + 1.453125)) + 1.57122564315795898437500, // sqrt(1.0 + (float)94/64.0) = sqrt(1.0 + 1.468750)) + 1.57618999481201171875000, // sqrt(1.0 + (float)95/64.0) = sqrt(1.0 + 1.484375)) + 1.58113884925842285156250, // sqrt(1.0 + (float)96/64.0) = sqrt(1.0 + 1.500000)) + 1.58607220649719238281250, // sqrt(1.0 + (float)97/64.0) = sqrt(1.0 + 1.515625)) + 1.59099030494689941406250, // sqrt(1.0 + (float)98/64.0) = sqrt(1.0 + 1.531250)) + 1.59589314460754394531250, // sqrt(1.0 + (float)99/64.0) = sqrt(1.0 + 1.546875)) + 1.60078108310699462890625, // sqrt(1.0 + (float)100/64.0) = sqrt(1.0 + 1.562500)) + 1.60565412044525146484375, // sqrt(1.0 + (float)101/64.0) = sqrt(1.0 + 1.578125)) + 1.61051237583160400390625, // sqrt(1.0 + (float)102/64.0) = sqrt(1.0 + 1.593750)) + 1.61535596847534179687500, // sqrt(1.0 + (float)103/64.0) = sqrt(1.0 + 1.609375)) + 1.62018513679504394531250, // sqrt(1.0 + (float)104/64.0) = sqrt(1.0 + 1.625000)) + 1.62500000000000000000000, // sqrt(1.0 + (float)105/64.0) = sqrt(1.0 + 1.640625)) + 1.62980055809020996093750, // sqrt(1.0 + (float)106/64.0) = sqrt(1.0 + 1.656250)) + 1.63458704948425292968750, // sqrt(1.0 + (float)107/64.0) = sqrt(1.0 + 1.671875)) + 1.63935959339141845703125, // sqrt(1.0 + (float)108/64.0) = sqrt(1.0 + 1.687500)) + 1.64411830902099609375000, // sqrt(1.0 + (float)109/64.0) = sqrt(1.0 + 1.703125)) + 1.64886319637298583984375, // sqrt(1.0 + (float)110/64.0) = sqrt(1.0 + 1.718750)) + 1.65359461307525634765625, // sqrt(1.0 + (float)111/64.0) = sqrt(1.0 + 1.734375)) + 1.65831243991851806640625, // sqrt(1.0 + (float)112/64.0) = sqrt(1.0 + 1.750000)) + 1.66301679611206054687500, // sqrt(1.0 + (float)113/64.0) = sqrt(1.0 + 1.765625)) + 1.66770803928375244140625, // sqrt(1.0 + (float)114/64.0) = sqrt(1.0 + 1.781250)) + 1.67238605022430419921875, // sqrt(1.0 + (float)115/64.0) = sqrt(1.0 + 1.796875)) + 1.67705094814300537109375, // sqrt(1.0 + (float)116/64.0) = sqrt(1.0 + 1.812500)) + 1.68170297145843505859375, // sqrt(1.0 + (float)117/64.0) = sqrt(1.0 + 1.828125)) + 1.68634223937988281250000, // sqrt(1.0 + (float)118/64.0) = sqrt(1.0 + 1.843750)) + 1.69096863269805908203125, // sqrt(1.0 + (float)119/64.0) = sqrt(1.0 + 1.859375)) + 1.69558250904083251953125, // sqrt(1.0 + (float)120/64.0) = sqrt(1.0 + 1.875000)) + 1.70018386840820312500000, // sqrt(1.0 + (float)121/64.0) = sqrt(1.0 + 1.890625)) + 1.70477271080017089843750, // sqrt(1.0 + (float)122/64.0) = sqrt(1.0 + 1.906250)) + 1.70934927463531494140625, // sqrt(1.0 + (float)123/64.0) = sqrt(1.0 + 1.921875)) + 1.71391367912292480468750, // sqrt(1.0 + (float)124/64.0) = sqrt(1.0 + 1.937500)) + 1.71846592426300048828125, // sqrt(1.0 + (float)125/64.0) = sqrt(1.0 + 1.953125)) + 1.72300612926483154296875, // sqrt(1.0 + (float)126/64.0) = sqrt(1.0 + 1.968750)) + 1.72753441333770751953125, // sqrt(1.0 + (float)127/64.0) = sqrt(1.0 + 1.984375)) + 1.73205077648162841796875, // sqrt(1.0 + (float)128/64.0) = sqrt(1.0 + 2.000000)) + 1.73655545711517333984375, // sqrt(1.0 + (float)129/64.0) = sqrt(1.0 + 2.015625)) + 1.74104857444763183593750, // sqrt(1.0 + (float)130/64.0) = sqrt(1.0 + 2.031250)) + 1.74553000926971435546875, // sqrt(1.0 + (float)131/64.0) = sqrt(1.0 + 2.046875)) + 1.75000000000000000000000, // sqrt(1.0 + (float)132/64.0) = sqrt(1.0 + 2.062500)) + 1.75445854663848876953125, // sqrt(1.0 + (float)133/64.0) = sqrt(1.0 + 2.078125)) + 1.75890588760375976562500, // sqrt(1.0 + (float)134/64.0) = sqrt(1.0 + 2.093750)) + 1.76334202289581298828125, // sqrt(1.0 + (float)135/64.0) = sqrt(1.0 + 2.109375)) + 1.76776695251464843750000, // sqrt(1.0 + (float)136/64.0) = sqrt(1.0 + 2.125000)) + 1.77218091487884521484375, // sqrt(1.0 + (float)137/64.0) = sqrt(1.0 + 2.140625)) + 1.77658379077911376953125, // sqrt(1.0 + (float)138/64.0) = sqrt(1.0 + 2.156250)) + 1.78097581863403320312500, // sqrt(1.0 + (float)139/64.0) = sqrt(1.0 + 2.171875)) + 1.78535711765289306640625, // sqrt(1.0 + (float)140/64.0) = sqrt(1.0 + 2.187500)) + 1.78972768783569335937500, // sqrt(1.0 + (float)141/64.0) = sqrt(1.0 + 2.203125)) + 1.79408752918243408203125, // sqrt(1.0 + (float)142/64.0) = sqrt(1.0 + 2.218750)) + 1.79843688011169433593750, // sqrt(1.0 + (float)143/64.0) = sqrt(1.0 + 2.234375)) + 1.80277562141418457031250, // sqrt(1.0 + (float)144/64.0) = sqrt(1.0 + 2.250000)) + 1.80710399150848388671875, // sqrt(1.0 + (float)145/64.0) = sqrt(1.0 + 2.265625)) + 1.81142210960388183593750, // sqrt(1.0 + (float)146/64.0) = sqrt(1.0 + 2.281250)) + 1.81572985649108886718750, // sqrt(1.0 + (float)147/64.0) = sqrt(1.0 + 2.296875)) + 1.82002747058868408203125, // sqrt(1.0 + (float)148/64.0) = sqrt(1.0 + 2.312500)) + 1.82431495189666748046875, // sqrt(1.0 + (float)149/64.0) = sqrt(1.0 + 2.328125)) + 1.82859230041503906250000, // sqrt(1.0 + (float)150/64.0) = sqrt(1.0 + 2.343750)) + 1.83285975456237792968750, // sqrt(1.0 + (float)151/64.0) = sqrt(1.0 + 2.359375)) + 1.83711731433868408203125, // sqrt(1.0 + (float)152/64.0) = sqrt(1.0 + 2.375000)) + 1.84136497974395751953125, // sqrt(1.0 + (float)153/64.0) = sqrt(1.0 + 2.390625)) + 1.84560286998748779296875, // sqrt(1.0 + (float)154/64.0) = sqrt(1.0 + 2.406250)) + 1.84983110427856445312500, // sqrt(1.0 + (float)155/64.0) = sqrt(1.0 + 2.421875)) + 1.85404956340789794921875, // sqrt(1.0 + (float)156/64.0) = sqrt(1.0 + 2.437500)) + 1.85825860500335693359375, // sqrt(1.0 + (float)157/64.0) = sqrt(1.0 + 2.453125)) + 1.86245810985565185546875, // sqrt(1.0 + (float)158/64.0) = sqrt(1.0 + 2.468750)) + 1.86664807796478271484375, // sqrt(1.0 + (float)159/64.0) = sqrt(1.0 + 2.484375)) + 1.87082874774932861328125, // sqrt(1.0 + (float)160/64.0) = sqrt(1.0 + 2.500000)) + 1.87500000000000000000000, // sqrt(1.0 + (float)161/64.0) = sqrt(1.0 + 2.515625)) + 1.87916207313537597656250, // sqrt(1.0 + (float)162/64.0) = sqrt(1.0 + 2.531250)) + 1.88331484794616699218750, // sqrt(1.0 + (float)163/64.0) = sqrt(1.0 + 2.546875)) + 1.88745856285095214843750, // sqrt(1.0 + (float)164/64.0) = sqrt(1.0 + 2.562500)) + 1.89159321784973144531250, // sqrt(1.0 + (float)165/64.0) = sqrt(1.0 + 2.578125)) + 1.89571881294250488281250, // sqrt(1.0 + (float)166/64.0) = sqrt(1.0 + 2.593750)) + 1.89983546733856201171875, // sqrt(1.0 + (float)167/64.0) = sqrt(1.0 + 2.609375)) + 1.90394330024719238281250, // sqrt(1.0 + (float)168/64.0) = sqrt(1.0 + 2.625000)) + 1.90804219245910644531250, // sqrt(1.0 + (float)169/64.0) = sqrt(1.0 + 2.640625)) + 1.91213226318359375000000, // sqrt(1.0 + (float)170/64.0) = sqrt(1.0 + 2.656250)) + 1.91621375083923339843750, // sqrt(1.0 + (float)171/64.0) = sqrt(1.0 + 2.671875)) + 1.92028641700744628906250, // sqrt(1.0 + (float)172/64.0) = sqrt(1.0 + 2.687500)) + 1.92435050010681152343750, // sqrt(1.0 + (float)173/64.0) = sqrt(1.0 + 2.703125)) + 1.92840611934661865234375, // sqrt(1.0 + (float)174/64.0) = sqrt(1.0 + 2.718750)) + 1.93245315551757812500000, // sqrt(1.0 + (float)175/64.0) = sqrt(1.0 + 2.734375)) + 1.93649172782897949218750, // sqrt(1.0 + (float)176/64.0) = sqrt(1.0 + 2.750000)) + 1.94052183628082275390625, // sqrt(1.0 + (float)177/64.0) = sqrt(1.0 + 2.765625)) + 1.94454360008239746093750, // sqrt(1.0 + (float)178/64.0) = sqrt(1.0 + 2.781250)) + 1.94855713844299316406250, // sqrt(1.0 + (float)179/64.0) = sqrt(1.0 + 2.796875)) + 1.95256245136260986328125, // sqrt(1.0 + (float)180/64.0) = sqrt(1.0 + 2.812500)) + 1.95655953884124755859375, // sqrt(1.0 + (float)181/64.0) = sqrt(1.0 + 2.828125)) + 1.96054840087890625000000, // sqrt(1.0 + (float)182/64.0) = sqrt(1.0 + 2.843750)) + 1.96452915668487548828125, // sqrt(1.0 + (float)183/64.0) = sqrt(1.0 + 2.859375)) + 1.96850192546844482421875, // sqrt(1.0 + (float)184/64.0) = sqrt(1.0 + 2.875000)) + 1.97246670722961425781250, // sqrt(1.0 + (float)185/64.0) = sqrt(1.0 + 2.890625)) + 1.97642350196838378906250, // sqrt(1.0 + (float)186/64.0) = sqrt(1.0 + 2.906250)) + 1.98037242889404296875000, // sqrt(1.0 + (float)187/64.0) = sqrt(1.0 + 2.921875)) + 1.98431348800659179687500, // sqrt(1.0 + (float)188/64.0) = sqrt(1.0 + 2.937500)) + 1.98824667930603027343750, // sqrt(1.0 + (float)189/64.0) = sqrt(1.0 + 2.953125)) + 1.99217212200164794921875, // sqrt(1.0 + (float)190/64.0) = sqrt(1.0 + 2.968750)) + 1.99608993530273437500000, // sqrt(1.0 + (float)191/64.0) = sqrt(1.0 + 2.984375)) + 2.00000000000000000000000 // sqrt(1.0 + (float)192/64.0) = sqrt(1.0 + 3.000000)) +}; + +#endif + +#endif diff --git a/firmware/src/Motherboard/Steppers.cc b/firmware/src/Motherboard/Steppers.cc index a3155c5..d9c9939 100644 --- a/firmware/src/Motherboard/Steppers.cc +++ b/firmware/src/Motherboard/Steppers.cc @@ -19,14 +19,94 @@ #include "Steppers.hh" #include "StepperAxis.hh" #include +#include #include #include #include +#include +#include +#include #include "EepromMap.hh" #include "Eeprom.hh" +#include "EepromDefaults.hh" +#ifdef SMALL_4K_RAM + #include "Errors.hh" +#endif #ifdef HAS_STEPPER_ACCELERATION #include "StepperAccel.hh" +#include "SqrtTable.hh" +#endif + +// Which master_steps to use when calculating the feed rate +#define MASTER_STEPS planner_master_steps_cfr + +#ifdef SMALL_4K_RAM + #define MINIMUM_FREE_SRAM 100 +#endif + +#if defined(STORE_RAM_USAGE_TO_EEPROM) || defined(SMALL_4K_RAM) + +//Stack checking +//http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=52249 +extern uint8_t _end; +extern uint8_t __stack; + +#define STACK_CANARY 0xc5 + +void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1"))); + +void StackPaint(void) +{ +#if 0 + uint8_t *p = &_end; + + while(p <= &__stack) + { + *p = STACK_CANARY; + p++; + } +#else + __asm volatile (" ldi r30,lo8(_end)\n" + " ldi r31,hi8(_end)\n" + " ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */ + " ldi r25,hi8(__stack)\n" + " rjmp .cmp\n" + ".loop:\n" + " st Z+,r24\n" + ".cmp:\n" + " cpi r30,lo8(__stack)\n" + " cpc r31,r25\n" + " brlo .loop\n" + " breq .loop"::); +#endif +} + + +uint16_t StackCount(void) +{ + const uint8_t *p = &_end; + uint16_t c = 0; + + while(*p == STACK_CANARY && p <= &__stack) + { + p++; + c++; + } + + return c; +} + +#endif + +#ifdef STORE_RAM_USAGE_TO_EEPROM + +void storeStackFreeToEeprom(void) +{ + uint16_t sc = StackCount(); + eeprom::putEepromUInt32(eeprom::RAM_USAGE_DEBUG, (uint32_t)sc); +} + #endif namespace steppers { @@ -37,18 +117,36 @@ int32_t intervals; volatile int32_t intervals_remaining; StepperAxis axes[STEPPER_COUNT]; volatile bool is_homing; +#ifdef HAS_STEPPER_ACCELERATION +FPTYPE axis_steps_per_unit_inverse[NUM_AXIS]; +#endif #ifdef HAS_STEPPER_ACCELERATION bool acceleration = false; bool planner = false; + bool strangled = false; uint8_t plannerMaxBufferSize; volatile bool force_acceleration_off = false; - Point lastTarget; + static Point lastTarget, droppedSegmentsRelative; + static int32_t droppedSegmentsUs; + bool accelerationSwitchBackLockout; + + // Segments are accelerated when segmentAccelState is true; unaccelerated otherwise + static bool segmentAccelState = true; + #endif bool holdZ = false; +//Also requires DEBUG_ZADVANCE to be defined in StepperAccel.h +//#define TIME_STEPPER_INTERRUPT + +#if defined(DEBUG_ZADVANCE) && defined(TIME_STEPPER_INTERRUPT) +uint16_t debugTimer = 0; +#endif + + bool isRunning() { return is_running || is_homing; } @@ -63,14 +161,17 @@ void init(Motherboard& motherboard) { for (int i = 0; i < STEPPER_COUNT; i++) { axes[i] = StepperAxis(motherboard.getStepperInterface(i)); } - - reset(); } void abort() { #ifdef HAS_STEPPER_ACCELERATION - if ( acceleration ) quickStop(); + if ( acceleration ) { + quickStop(); + lastTarget = getCurrentPosition(); + } #endif + if (( is_homing ) && ( acceleration ) && ( force_acceleration_off )) switchToAcceleratedDriver(); + is_running = false; is_homing = false; } @@ -82,13 +183,46 @@ float convertAxisMMToFloat(int64_t st ) { return aspmf; } +//Returns true if the eeprom contains sane values +//We check Jog Mode is 0, 1 or 2 and STEPS_PER_MM_B is STEPS_PER_MM_B_DEFAULT +//Note that STEPS_PER_MM_B is currently unused. If we ever use STEPS_PER_MM_B, we will want to find +//another method or just rely on JogMode. +//This is mainly a solution that gets around issues with RobG's firmware conflicting with this one. + +bool eepromIsSane() { + uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS); + jogModeSettings = jogModeSettings >> 1; + + //Valid values are 0,1 or 2, anything else means we have corruption + if ( jogModeSettings > 2 ) return false; + + if ( eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_B) != EEPROM_DEFAULT_STEPS_PER_MM_B ) + return false; + + return true; +} + void reset() { #ifdef HAS_STEPPER_ACCELERATION + if ( ! eepromIsSane() ) eeprom::setJettyFirmwareDefaults(); + //Get the acceleration settings - uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, EEPROM_DEFAULT_STEPPER_DRIVER); acceleration = accel & 0x01; planner = (accel & 0x02)?true:false; + strangled = (accel & 0x04)?true:false; + + if ( strangled ) { + //When in strangled mode, we use the accelerated drive, so we set + //that to true, but we switch SegmentAccelState to false, because we + //want non-accelerated segments + + acceleration = true; + setSegmentAccelState(false); + } else { + setSegmentAccelState(true); + } if ( acceleration ) { //Good description of the settings can be found here: @@ -104,60 +238,95 @@ void reset() { //Same as axis steps:mm in the firmware //Temporarily placed here, should be read from eeprom - axis_steps_per_unit[X_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_X, STEPS_PER_MM_X_DEFAULT)); - axis_steps_per_unit[Y_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Y, STEPS_PER_MM_Y_DEFAULT)); - axis_steps_per_unit[Z_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Z, STEPS_PER_MM_Z_DEFAULT)); - axis_steps_per_unit[E_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM,44) / 10.0; + axis_steps_per_unit[X_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_X, EEPROM_DEFAULT_STEPS_PER_MM_X)); + axis_steps_per_unit[Y_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Y, EEPROM_DEFAULT_STEPS_PER_MM_Y)); + axis_steps_per_unit[Z_AXIS] = convertAxisMMToFloat(eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Z, EEPROM_DEFAULT_STEPS_PER_MM_Z)); + axis_steps_per_unit[E_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM) / 10.0; + + for ( uint8_t i = 0; i < NUM_AXIS; i ++ ) + axis_steps_per_unit_inverse[i] = FTOFP(1.0 / axis_steps_per_unit[i]); //M201 - Set max acceleration in units/s^2 for print moves // X, Y, Z, E maximum start speed for accelerated moves. E default values are good for skeinforge 40+, for older versions raise them a lot. - max_acceleration_units_per_sq_second[X_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, 2000); - max_acceleration_units_per_sq_second[Y_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, 2000); - max_acceleration_units_per_sq_second[Z_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, 150); - max_acceleration_units_per_sq_second[E_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, 60000); + max_acceleration_units_per_sq_second[X_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_X); + max_acceleration_units_per_sq_second[Y_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Y); + max_acceleration_units_per_sq_second[Z_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Z); + max_acceleration_units_per_sq_second[E_AXIS] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_A); for (uint8_t i = 0; i < NUM_AXIS; i ++) - axis_steps_per_sqr_second[i] = max_acceleration_units_per_sq_second[i] * axis_steps_per_unit[i]; + axis_steps_per_sqr_second[i] = (uint32_t)((float)max_acceleration_units_per_sq_second[i] * axis_steps_per_unit[i]); //M203 - Set maximum feedrate that your machine can sustain in mm/sec - max_feedrate[X_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, 160); - max_feedrate[Y_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, 160); - max_feedrate[Z_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, 10); - max_feedrate[E_AXIS] = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, 100); + max_feedrate[X_AXIS] = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X)); + max_feedrate[Y_AXIS] = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y)); + max_feedrate[Z_AXIS] = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Z)); + max_feedrate[E_AXIS] = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_A)); //M204 - Set default accelerationm for "Normal Moves (acceleration)" and "filament only moves (retraction)" in mm/sec^2 // X, Y, Z and E max acceleration in mm/s^2 for printing moves - p_acceleration = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, 5000); + p_acceleration = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_NORM)); // X, Y, Z and E max acceleration in mm/s^2 for r retracts - p_retract_acceleration = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, 3000); + p_retract_acceleration = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_RETRACT)); //M205 - Advanced Settings //minimumfeedrate - minimum travel speed while printing - minimumfeedrate = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,0) / 10.0; + minimumfeedrate = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE, EEPROM_DEFAULT_ACCEL_MIN_FEED_RATE) / 10.0); //mintravelfeedrate - minimum travel speed while travelling - mintravelfeedrate = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,0) / 10.0; + mintravelfeedrate = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE, EEPROM_DEFAULT_ACCEL_MIN_TRAVEL_FEED_RATE) / 10.0); + + extruder_deprime_steps = (int32_t)(((float)eeprom::getEepromUInt32(eeprom::ACCEL_EXTRUDER_DEPRIME, EEPROM_DEFAULT_ACCEL_EXTRUDER_DEPRIME) / 10.0) * axis_steps_per_unit[E_AXIS]); + + //Maximum speed change + max_speed_change[X_AXIS] = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_X, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_X) / 10.0); + max_speed_change[Y_AXIS] = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Y, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Y) / 10.0); + max_speed_change[Z_AXIS] = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Z, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Z) / 10.0); + max_speed_change[E_AXIS] = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_A, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_A) / 10.0); + +#ifdef YET_ANOTHER_JERK +#ifdef FIXED + smallest_max_speed_change = max_speed_change[Z_AXIS]; + for (uint8_t i = 0; i < NUM_AXIS; i++) + if ( max_speed_change[i] < smallest_max_speed_change ) + smallest_max_speed_change = max_speed_change[i]; +#endif +#endif + FPTYPE advanceK = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K, EEPROM_DEFAULT_ACCEL_ADVANCE_K) / 100000.0); + FPTYPE advanceK2 = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K2, EEPROM_DEFAULT_ACCEL_ADVANCE_K2) / 100000.0); + //UNUSED + //FPTYPE noodleDiameter = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_NOODLE_DIAMETER, EEPROM_DEFAULT_ACCEL_NOODLE_DIAMETER) / 100.0); - //max_xy_jerk - maximum xy jerk (mm/sec) - max_xy_jerk = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_XY_JERK,2) / 10.0; - max_xy_jerk_squared = max_xy_jerk * max_xy_jerk; + minimumSegmentTime = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_SEGMENT_TIME, EEPROM_DEFAULT_ACCEL_MIN_SEGMENT_TIME) / 10000.0); - //max_z_jerk - maximum z jerk (mm/sec) - max_z_jerk = (float)eeprom::getEepromUInt32(eeprom::ACCEL_MAX_Z_JERK,100) / 10.0; + // Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end + // of the buffer and all stops. This should not be much greater than zero and should only be changed + // if unwanted behavior is observed on a user's machine when running at very slow speeds. + minimumPlannerSpeed = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_MIN_PLANNER_SPEED, EEPROM_DEFAULT_ACCEL_MIN_PLANNER_SPEED)); - float advanceK = (float)eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K,50) / 100000.0; - float filamentDiameter = (float)eeprom::getEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER,175) / 100.0; + extruder_only_max_feedrate = FTOFP((float)eeprom::getEepromUInt32(eeprom::ACCEL_REV_MAX_FEED_RATE, EEPROM_DEFAULT_ACCEL_REV_MAX_FEED_RATE)); + slowdown_limit = (int)eeprom::getEepromUInt32(eeprom::ACCEL_SLOWDOWN_LIMIT, EEPROM_DEFAULT_ACCEL_SLOWDOWN_LIMIT); + if ( slowdown_limit > (BLOCK_BUFFER_SIZE / 2)) slowdown_limit = 0; + + if (eeprom::getEepromUInt32(eeprom::ACCEL_CLOCKWISE_EXTRUDER, EEPROM_DEFAULT_ACCEL_CLOCKWISE_EXTRUDER) == 0) clockwise_extruder = false; + else clockwise_extruder = true; + force_acceleration_off = false; if ( planner ) plannerMaxBufferSize = BLOCK_BUFFER_SIZE - 1; else plannerMaxBufferSize = 1; - plan_init(advanceK, filamentDiameter, axis_steps_per_unit[E_AXIS]); //Initialize planner - st_init(); //Initialize stepper + plan_init(advanceK, advanceK2, holdZ); //Initialize planner + st_init(); //Initialize stepper + droppedSegmentsRelative[0] = 0; + droppedSegmentsRelative[1] = 0; + droppedSegmentsRelative[2] = 0; + droppedSegmentsRelative[3] = 0; + droppedSegmentsRelative[4] = 0; + droppedSegmentsUs = 0; lastTarget = Point(st_get_position(X_AXIS), st_get_position(Y_AXIS), st_get_position(Z_AXIS), st_get_position(E_AXIS), 0); } else lastTarget = getPosition(); @@ -181,7 +350,8 @@ void definePosition(const Point& position) { #endif } -/// Get current position +/// Get position +/// When accelerated, this is the target position of the command at the end of the pipeline const Point getPosition() { #if STEPPER_COUNT > 3 #ifdef HAS_STEPPER_ACCELERATION @@ -189,7 +359,11 @@ const Point getPosition() { return lastTarget; else #endif +#if STEPPER_COUNT > 4 return Point(axes[0].position,axes[1].position,axes[2].position,axes[3].position,axes[4].position); +#else + return Point(axes[0].position,axes[1].position,axes[2].position,axes[3].position, 0); +#endif #else #ifdef HAS_STEPPER_ACCELERATION if (( acceleration ) && ( ! force_acceleration_off )) @@ -200,6 +374,36 @@ const Point getPosition() { #endif } +/// Get current position +/// When accelerated, this is the position right now +/// advisable to call drainAccelerationBuffer before calling this +/// Only called by Control Panel + +const Point getCurrentPosition() { +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + Point p; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + p = Point(st_get_position(X_AXIS), st_get_position(Y_AXIS), st_get_position(Z_AXIS), st_get_position(E_AXIS), 0); + } + return p; + } +#endif + return getPosition(); +} + +#ifdef HAS_STEPPER_ACCELERATION + +//Drains the acceleration buffer down to empty before returning + +void drainAccelerationBuffer(void) { + if (( acceleration ) && ( ! force_acceleration_off )) { + while ( ! st_empty() ) _delay_us(10); + } +} + +#endif + void setHoldZ(bool holdZ_in) { holdZ = holdZ_in; } @@ -208,42 +412,364 @@ void setHoldZ(bool holdZ_in) { //Calculates the feedrate based on moving from "from" to "to" -float calcFeedRate(const Point& from, const Point& to, int32_t interval ) { - +FPTYPE calcFeedRate(const Point& from, const Point& to, int32_t interval, bool includeEAxis, + int32_t& zremainder ) { //Calculate the distance in mm's by: //Calculate the delta distances and convert to mm's //Then sqr of sum of squares of the deltas for the distance - //We also calculate at the same time, master_steps in steps by finding the dominant axis (for all 5) + //We also calculate at the same time, planner_master_steps in steps by finding the dominant axis (for all 5) //You would think it would be for X/Y/Z only, but they "mistakenly" use all 5 in rep g. //so we do the same here for consistancy - int32_t master_steps = 0; - float distance = 0.0; + // planner_distance_cfr -- May or may not include the A & B axes + // planner_distance -- Includes ALL axes - for (uint8_t i = 0; i < AXIS_COUNT; i ++ ) { - int32_t delta = to[i] - from[i]; - if ( delta < 0 ) delta *= -1; +#ifdef FIXED + FPTYPE master_delta ; // Initialized later + uint8_t master_delta_index; // Initialized later + FPTYPE master_delta_cfr = 0; + uint8_t master_delta_index_cfr = 0; +#endif - if ( delta > master_steps ) master_steps = delta; + // Handle the X, Y, and Z axes + + bool override_master_steps = false; + uint32_t planner_master_steps_cfr = 0; + FPTYPE planner_distance_cfr = 0; + + zremainder = 0; + + for ( uint8_t i = 0; i <= Z_AXIS; i++ ) + { + planner_steps[i] = to[i] - from[i]; + if ( planner_steps[i] != 0 ) + { +#ifdef FIXED +#define MAX_STEPS 0x7000 +// For testing on a ToM, try a MAX_STEPS which will trigger at 4 cm of Z axis travel @ 200 steps/mm +//#define MAX_STEPS ( 40 * 200 ) + // Step counts in excess of +0x7fff or -0x8000 will + // overflow an _Accum's "whole" part field which is + // a int16_t. Using 0x7000 is a bit conservative + if (planner_steps[i] > MAX_STEPS) + { + // This is so infrequent, that we'll take the float hit + // Besides, we're dealing with a value to large for an _Accum + if ( i == Z_AXIS ) + { + // We are doing a large travel on a high resolution Z axis + // Let's break this move into several smaller moves. We don't + // actually do this "correctly" as we take all the X, Y, and E + // steps in the first submove. Thus this is a hack intended + // only for large Z-axis travel situations. + + // Unexercised Z-axis steps which will remain once we truncate + // this move to MAX_STEPS steps + + zremainder = planner_steps[Z_AXIS] - MAX_STEPS; + } + planner_steps[i] = MAX_STEPS; + } + else if (planner_steps[i] < -(MAX_STEPS)) + { + // This is so infrequent, that we'll take the float hit + // Besides, we're dealing with a value to large for an _Accum + if ( i == Z_AXIS ) + { + // We are doing a large travel on a high resolution Z axis + // Let's break this move into several smaller moves. We don't + // actually do this "correctly" as we take all the X, Y, and E + // steps in the first submove. Thus this is a hack intended + // only for large Z-axis travel situations. + + // Unexercised Z-axis steps which will remain once we truncate + // this move to MAX_STEPS steps + + zremainder = planner_steps[Z_AXIS] + MAX_STEPS; + } + planner_steps[i] = -(MAX_STEPS); + } +#endif + delta_mm[i] = FPMULT2(ITOFP(planner_steps[i]), axis_steps_per_unit_inverse[i]); + planner_steps[i] = labs(planner_steps[i]); +#ifdef FIXED + if ( FPABS(delta_mm[i]) > master_delta_cfr ) + { + master_delta_index_cfr = i; + master_delta_cfr = FPABS(delta_mm[i]); + } +#else + planner_distance_cfr += FPSQUARE(delta_mm[i]); +#endif + if ( (uint32_t)planner_steps[i] > planner_master_steps_cfr ) + planner_master_steps_cfr = (uint32_t)planner_steps[i]; + } + else + delta_mm[i] = 0; + } - const float delta_mm = (float)delta / axis_steps_per_unit[i]; - distance += delta_mm * delta_mm; + // For an extruder only move, force includeEAxis to be true + if ( planner_master_steps_cfr == 0 ) + { + includeEAxis = true; + override_master_steps = true; } - distance = sqrt(distance); - return (distance * 60000000.0) / ((float)interval * (float)master_steps); + // Handle the extruder axes + planner_master_steps = planner_master_steps_cfr; +#ifdef FIXED + master_delta_index = master_delta_index_cfr; + master_delta = master_delta_cfr; +#else + planner_distance = planner_distance_cfr; +#endif + + for ( uint8_t i = Z_AXIS+1; i < NUM_AXIS; i++ ) + { + planner_steps[i] = to[i] - from[i]; + if ( planner_steps[i] != 0 ) + { +#ifdef FIXED + // Step counts in excess of +0x7fff or -0x8000 will + // overflow an _Accum's "whole" part field which is + // a int16_t. Using 0x7000 is a bit conservative + if (planner_steps[i] > 0x7000) planner_steps[i] = 0x7000; + else if (planner_steps[i] < -(MAX_STEPS)) planner_steps[i] = -(MAX_STEPS); +#endif + delta_mm[i] = FPMULT2(ITOFP(planner_steps[i]), axis_steps_per_unit_inverse[i]); + planner_steps[i] = labs(planner_steps[i]); +#ifdef FIXED + if ( FPABS(delta_mm[i]) > master_delta ) + { + master_delta_index = i; + master_delta = FPABS(delta_mm[i]); + } +#else + planner_distance += FPSQUARE(delta_mm[i]); +#endif + if ( (uint32_t)planner_steps[i] > planner_master_steps ) + planner_master_steps = (uint32_t)planner_steps[i]; + } + else + delta_mm[i] = 0; + } + + // If we forced includeEAxis on (because this is an extruder only move) then + // also use planner_master_steps for planner_master_steps_cfr (which is zero) + + if ( override_master_steps ) planner_master_steps_cfr = planner_master_steps; + + + // planner_distance is now a value between 0 and 3. We want to know + // + // sqrt(1+planner_distance)*delta_mm[master_delta_index] + // + // Our lookup table will give us table[(int)(x * SQRT_TABLE_RESOLUTION)] = sqrt(1 + x), 0 <= x <= 3 + + if ( includeEAxis ) + { + // All distances include the A & B axes + // planner_distance_cfr == planner_distance + // Just compute planner_distance and then set planner_distance_cfr = planner_distance +#ifdef FIXED + FPTYPE planner_distance_sq = 0; + for ( uint8_t i = 0; i < NUM_AXIS; i++ ) + { + if ( (i == master_delta_index) || (delta_mm[i] == 0) ) continue; + planner_distance_sq += FPSQUARE(FPDIV(delta_mm[i], master_delta)); + } + + uint8_t j = FPTOI(planner_distance_sq << SQRT_TABLE_SHIFT); + planner_distance = FPMULT2( +#ifdef SMALL_4K_RAM + pgm_read_dword_near(&sqrt_table[j]), +#else + sqrt_table[j], +#endif + master_delta); +#else + planner_distance = FPSQRT(planner_distance); +#endif + planner_distance_cfr = planner_distance; + } + else + { + // We want planner_distance_cfr to NOT include the A or B axes (E_AXIS) +#ifdef FIXED + FPTYPE planner_distance_sq; + + // First tackle planner_distance_cfr + + // The (x,y,z) calc + planner_distance_sq = 0; + for ( uint8_t i = 0; i <= Z_AXIS; i++ ) + { + if ( (i == master_delta_index_cfr) || (delta_mm[i] == 0) ) continue; + planner_distance_sq += FPSQUARE(FPDIV(delta_mm[i], master_delta_cfr)); + } + + uint8_t j = FPTOI(planner_distance_sq << SQRT_TABLE_SHIFT); + planner_distance_cfr = FPMULT2( +#ifdef SMALL_4K_RAM + pgm_read_dword_near(&sqrt_table[j]), +#else + sqrt_table[j], +#endif + master_delta_cfr); + + // Now include the A & B axes for planner_distance + // If master_index == master_index_cfr then we can use the prior calcs as a starting point + // Also master_delta == master_delta_cfr (which is significant when we get the the square root calc + + if ( master_delta_index == master_delta_index_cfr ) + { + for ( uint8_t i = Z_AXIS+1; i < NUM_AXIS; i++ ) + { + if ( (i == master_delta_index_cfr) || (delta_mm[i] == 0) ) continue; + planner_distance_sq += FPSQUARE(FPDIV(delta_mm[i], master_delta_cfr)); + } + } + else + { + // master_index != master_index_cfr + // master_delta != master_delta_cfr + + // This means that the longest distance component was the A or B axis + // We need to recompute the sum of the squares with a different normalization + + planner_distance_sq = 0; + for ( uint8_t i = 0; i < NUM_AXIS; i++ ) + { + if ( (i == master_delta_index) || (delta_mm[i] == 0) ) continue; + planner_distance_sq += FPSQUARE(FPDIV(delta_mm[i], master_delta)); + } + } + + j = FPTOI(planner_distance_sq << SQRT_TABLE_SHIFT); + planner_distance = FPMULT2( +#ifdef SMALL_4K_RAM + pgm_read_dword_near(&sqrt_table[j]), +#else + sqrt_table[j], +#endif + master_delta); +#else + planner_distance_cfr = FPSQRT(planner_distance_cfr); + planner_distance = FPSQRT(planner_distance); +#endif + } + +// It's bad news if we get to here without MASTER_STEPS being defined +#ifndef MASTER_STEPS +#define MASTER_STEPS planner_master_steps_cfr +#endif + + // Now compute the feed rate +#ifdef FIXED + if ( (interval <= 0) || (MASTER_STEPS == 0) ) return 0; + // if (interval >= 0 && interval <= 0x7fff && MASTER_STEPS <= 0x7fff) + else if ((((uint32_t)interval | (uint32_t)MASTER_STEPS) & 0xffff8000) == 0) { + + // We can convert interval to an _Accum and then shift it entirely to the right of the + // fixed decimal point without any loss of precision. This ammounts to dividing + // interval by 2^16. At the same time, we multiply planner_distance by 1000000 / 2^16, + // again with no loss of precision. Since we've divided both the numerator and denominator + // by 2^16, we still get the correct result. + + // This code case is expected to be the predominant case -- we want it to be fast + + return FPDIV(FPMULT2(planner_distance_cfr, KCONSTANT_1000000_LSR_16), + FPMULT2(ITOFP((int32_t)MASTER_STEPS), (ITOFP((int32_t)interval)>>16))); + + } + // else if (interval >= 0 && interval <= 0x7fffff && MASTER_STEPS <= 0x7fffff) + else if ((((uint32_t)interval | (uint32_t)MASTER_STEPS) & 0xff800000) == 0) { + + // interval or MASTER_STEPS (or both) are too large to play the trick + // we did above. Their product may still overflow an _Accum. So, we shift each + // one over until it is <= 0x7fff, keeping track of how many bits we shifted. + // Then we do the same trick as above but shift by only >> (16-i) where i is the + // number of bits we pre-shifted interval and MASTER_STEPS by. + + // For this case we do have loss of precision, but it should be pretty minor as these + // are large numbers. + + // This case should be an infrequent case, likely only happening for large rafts or + // other very large, slow moves. As such, the code being a little slower here should + // not be too much of a problem as the printer is moving slow which allows the pipeline + // planner to be slower as well. + + // Of course, a machine with LOTS of steps/mm for a given axes might generate large + // MASTER_STEPS. + + uint8_t n = 16; + while (interval > 0x7fff) { + interval >>= 1; + n--; + } + while (MASTER_STEPS > 0x7fff) { + MASTER_STEPS >>= 1; + n--; + } + + // Since both interval and MASTER_STEPS were <= 0x7fffff + // when we started, we know that n >= 0. In fact, it's >= 2. + // At any rate, we won't do ">> (negative-number)". + + // So, yes we could have started with interval <= 0xffffff and MASTER_STEPS <= 0xffffff + + return FPDIV(FPMULT2(planner_distance_cfr, KCONSTANT_1000000_LSR_16), + FPMULT2(ITOFP((int32_t)MASTER_STEPS), (ITOFP((int32_t)interval) >> n))); + } + else { + + // interval or MASTER_STEPS or both are really large numbers OR interval < 0 + // (in which case 0 != (interval & 0x80000000). Just go ahead and pay the penalty of + // conversion to and from floats. + + // This case is not expected to happen in practice + + return FTOFP(((FPTOF(planner_distance_cfr) * 1000000.0) / + ((float)MASTER_STEPS * (float)interval))); + } +#else + planner_distance = FPSQRT(planner_distance_cfr); + if ( (interval <= 0) || (MASTER_STEPS == 0) ) return 0; + return ((planner_distance_cfr * 1000000.0) / ((FPTYPE)interval * (FPTYPE)MASTER_STEPS)); +#endif } #endif void setTarget(const Point& target, int32_t dda_interval) { #ifdef HAS_STEPPER_ACCELERATION - if (( acceleration ) && ( ! force_acceleration_off )) { - float feedRate = calcFeedRate(lastTarget, target, dda_interval ); + //BUG WORKAROUND FOR CASES WHERE ACCELERATION DOESN'T COME BACK AFTER HOMING + if (( acceleration ) && ( force_acceleration_off ) && ( ! accelerationSwitchBackLockout ) && ( target[3] != lastTarget[3] )) switchToAcceleratedDriver(); - //Figure out the distance between x1,y1 and x2,y2 using pythagoras - plan_buffer_line(target[0], target[1], target[2], target[3], feedRate, 0); + if (( acceleration ) && ( ! force_acceleration_off )) { + int32_t zremainder = 0; + do { + int32_t ztarget; + FPTYPE feedRate = calcFeedRate(lastTarget, target, dda_interval, true, zremainder); + if ( zremainder != 0 ) + { + // In this case, plan_buffer_line() is assured to return True + // since the Z-axis motion exceeds 0x7000 steps + ztarget = target[2] - zremainder; + } + else + ztarget = target[2]; + //Figure out the distance between x1,y1 and x2,y2 using pythagoras + if ( plan_buffer_line(target[0], target[1], ztarget, target[3], feedRate, 0, segmentAccelState) ) + lastTarget = target; + if ( zremainder != 0 ) + // Actually it's okay to always do this step; however, it may be slower to always do it + // so we only do it when zremainder != 0 + lastTarget[2] = ztarget; + if ( movesplanned() >= plannerMaxBufferSize) is_running = true; + else is_running = false; + } while ( zremainder != 0 ); } else { #endif int32_t max_delta = 0; @@ -251,7 +777,7 @@ void setTarget(const Point& target, int32_t dda_interval) { axes[i].setTarget(target[i], false); const int32_t delta = axes[i].delta; // Only shut z axis on inactivity - if (i == 2 && !holdZ) axes[i].enableStepper(delta != 0); + if (i == Z_AXIS && !holdZ) axes[i].enableStepper(delta != 0); else if (delta != 0) axes[i].enableStepper(true); if (delta > max_delta) { max_delta = delta; @@ -267,23 +793,40 @@ void setTarget(const Point& target, int32_t dda_interval) { is_running = true; #ifdef HAS_STEPPER_ACCELERATION - } + lastTarget = target; - lastTarget = target; + } #endif } void setTargetNew(const Point& target, int32_t us, uint8_t relative) { #ifdef HAS_STEPPER_ACCELERATION + //BUG WORKAROUND FOR CASES WHERE ACCELERATION DOESN'T COME BACK AFTER HOMING + if (( acceleration ) && ( force_acceleration_off ) && ( ! accelerationSwitchBackLockout ) && + ((((relative & (1 << 3)) != 0) && ( target[3] != 0 )) || + (((relative & (1 << 3)) == 0) && ( target[3] != lastTarget[3])))) + switchToAcceleratedDriver(); + if (( acceleration ) && ( ! force_acceleration_off )) { Point newPosition = target; for (uint8_t i = 0; i < AXIS_COUNT; i ++) { - if ((relative & (1 << i)) != 0) + if ((relative & (1 << i)) != 0) { newPosition[i] = lastTarget[i] + target[i]; + + //Added on any relative move we have saved from a previous dropped segments + //Only if we're relative, if we're absolute we don't care + newPosition[i] += droppedSegmentsRelative[i]; + } } + us += droppedSegmentsUs; int32_t max_delta = 0; for (int i = 0; i < AXIS_COUNT; i++) { + // Do not include extruder steps in max_delta unless this is an extruder-only move + // More correct code would allow for two extruders. However, it's not as simple + // as "i >= E_AXIS" as that would only pick up the first extruder e-steps and not + // the larger of the two extruder e-steps. + if (i == E_AXIS && max_delta != 0) continue; int32_t delta = newPosition[i] - lastTarget[i]; if ( delta < 0 ) delta *= -1; if (delta > max_delta) { @@ -291,17 +834,55 @@ void setTargetNew(const Point& target, int32_t us, uint8_t relative) { } } int32_t dda_interval = us / max_delta; - float feedRate = calcFeedRate(lastTarget, newPosition, dda_interval); + int32_t zremainder = 0; + do + { + int32_t ztarget; + FPTYPE feedRate = calcFeedRate(lastTarget, newPosition, dda_interval, false, zremainder); + if ( zremainder != 0 ) + { + // In this case, the count of z-steps exceeds 0x7000 and as such + // plan_buffer_line() will return True + ztarget = newPosition[2] - zremainder; + } + else + ztarget = newPosition[2]; + if ( plan_buffer_line(newPosition[0], newPosition[1], ztarget, newPosition[3], feedRate, 0, segmentAccelState) ) { + lastTarget = newPosition; + droppedSegmentsRelative = Point(0,0,0,0,0); + droppedSegmentsUs = 0; + if ( zremainder != 0) + // Actually it's okay to always do this step; however, it may be slower to always do it + // so we only do it when zremainder != 0 + lastTarget[2] = ztarget; + } else { + //Because we've dropped segments, we need to add on a "relative" version + //of the dropped move onto droppedSegmentsRelative for possible later use, if we + //do another relative move for that axis, otherwise relative moves can be lost. + //Relative moves are added to the existing value, absolute moves have lastTarget + //subtracted. + //All information is stored in "relative" co-ords + + // NOTE BENE: This code section should NOT need handling for r.zsteps != 0 since + // plan_buffer_line() should have returned True when r.zsteps != 0 - plan_buffer_line(newPosition[0], newPosition[1], newPosition[2], newPosition[3], feedRate, 0); - lastTarget = newPosition; + for (uint8_t i = 0; i < AXIS_COUNT; i ++) { + if ((relative & (1 << i)) != 0) + droppedSegmentsRelative[i] += target[i]; + else droppedSegmentsRelative[i] = target[i] - lastTarget[i]; + } + droppedSegmentsUs += us; + } + if ( movesplanned() >= plannerMaxBufferSize) is_running = true; + else is_running = false; + } while ( zremainder != 0 ); } else { #endif for (int i = 0; i < AXIS_COUNT; i++) { axes[i].setTarget(target[i], (relative & (1 << i)) != 0); // Only shut z axis on inactivity const int32_t delta = axes[i].delta; - if (i == 2 && !holdZ) { + if (i == Z_AXIS && !holdZ) { axes[i].enableStepper(delta != 0); } else if (delta != 0) { axes[i].enableStepper(true); @@ -326,51 +907,74 @@ void setTargetNew(const Point& target, int32_t us, uint8_t relative) { } #ifdef HAS_STEPPER_ACCELERATION -void switchToRegularDriver() { - if ( ! acceleration ) return; +void switchToRegularDriver(bool lockout) { - cli(); +#ifdef STORE_RAM_USAGE_TO_EEPROM + storeStackFreeToEeprom(); +#endif - //Get the current position of the accelerated driver and - //store it in the regular driver - Point currentPosition = getPosition(); - for (int i = 0; i < STEPPER_COUNT; i++) { - axes[i].definePosition(currentPosition[i]); - } + accelerationSwitchBackLockout = lockout; + if (( ! acceleration ) || ( acceleration && force_acceleration_off )) return; + + //Wait for the buffer to empty + drainAccelerationBuffer(); + + ATOMIC_BLOCK(ATOMIC_FORCEON) { + is_running = false; - force_acceleration_off = true; + //Get the current position of the accelerated driver and + //store it in the regular driver + Point currentPosition = getPosition(); + for (int i = 0; i < STEPPER_COUNT; i++) { + axes[i].definePosition(currentPosition[i]); + } - //Change the interrupt frequency to the regular driver - Motherboard::getBoard().setupFixedStepperTimer(); + force_acceleration_off = true; - sei(); + //Change the interrupt frequency to the regular driver + Motherboard::getBoard().setupFixedStepperTimer(); + } } void switchToAcceleratedDriver() { - if ( ! acceleration ) return; + accelerationSwitchBackLockout = false; + + if (( ! acceleration ) || ( acceleration && ( ! force_acceleration_off ))) return; //Get the current position of the regular driver and //store it in the accelerated driver - cli(); + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + force_acceleration_off = false; + if ( ! strangled ) setSegmentAccelState(true); - force_acceleration_off = false; + Point currentPosition = Point(axes[0].position, axes[1].position, axes[2].position, axes[3].position, axes[4].position); + definePosition(currentPosition); - Point currentPosition = Point(axes[0].position, axes[1].position, axes[2].position, axes[3].position, axes[4].position); - definePosition(currentPosition); + //Change the interrupt frequency to the accelerated driver + Motherboard::getBoard().setupAccelStepperTimer(); + } +} - //Change the interrupt frequency to the accelerated driver - Motherboard::getBoard().setupAccelStepperTimer(); - - sei(); +bool isAccelerated(void) { + if ( acceleration && ( ! force_acceleration_off )) return true; + return false; } + #endif /// Start homing void startHoming(const bool maximums, const uint8_t axes_enabled, const uint32_t us_per_step) { - +#ifdef SMALL_4K_RAM + uint16_t sc = StackCount(); +#if defined (__AVR_ATmega2560__) + sc -= 4096; //Take of 4K to simulate 644p +#endif + if ( sc < MINIMUM_FREE_SRAM) + Motherboard::getBoard().indicateError(ERR_NO_FREE_SRAM); +#endif #ifdef HAS_STEPPER_ACCELERATION - if ( acceleration ) switchToRegularDriver(); + if (( acceleration ) && (! force_acceleration_off )) switchToRegularDriver(false); #endif intervals_remaining = INT32_MAX; @@ -416,24 +1020,66 @@ bool isAtMinimum(uint8_t index) { return false; } +#ifdef DEBUG_ZADVANCE + void doLcd() { + Motherboard::getBoard().lcd.setCursor(0,0); + Motherboard::getBoard().lcd.writeFloat((float)zadvance,3); + Motherboard::getBoard().lcd.write(' '); + Motherboard::getBoard().lcd.setCursor(0,3); + Motherboard::getBoard().lcd.writeFloat((float)zadvance2,3); + Motherboard::getBoard().lcd.write(' '); // Motherboard::getBoard().lcd.setCursor(0,3); // Motherboard::getBoard().lcd.writeFloat((float)movesplanned(),1); // Motherboard::getBoard().lcd.write(' '); } -bool doInterrupt() { +#endif + + +void runSteppersSlice() { + //Set Foo and Bar pins for acceleration information #ifdef HAS_STEPPER_ACCELERATION if (( acceleration ) && ( ! force_acceleration_off )) { - //st_interrupt runs all the time, is_running reflects if we have space in the buffer or not, - //i.e. it enables us to add more commands if it's false + #ifdef INTERFACE_BAR_PIN + INTERFACE_BAR_PIN.setValue(true); + #endif + + uint8_t bufferUsed = movesplanned(); + + //Set foo based on how empty the pipeline command buffer is + //Less than 2 or less items left, and we switch foo off, otherwise on + #ifdef INTERFACE_FOO_PIN + if ( bufferUsed < 3 ) INTERFACE_FOO_PIN.setValue(false); + else INTERFACE_FOO_PIN.setValue(true); + #endif + } else { +#endif + #ifdef INTERFACE_BAR_PIN + INTERFACE_BAR_PIN.setValue(false); + #endif +#ifdef HAS_STEPPER_ACCELERATION + } +#endif - if ( movesplanned() >= plannerMaxBufferSize) is_running = true; - else is_running = false; + //Set the stepper interrupt timer +#if defined(DEBUG_ZADVANCE) && defined(TIME_STEPPER_INTERRUPT) + zadvance2 = debugTimer; +#endif +} - st_interrupt(); - - return is_running; + +void doInterrupt() { +#if defined(DEBUG_ZADVANCE) && defined(TIME_STEPPER_INTERRUPT) + DEBUG_TIMER_START; +#endif + +#ifdef HAS_STEPPER_ACCELERATION + if (( acceleration ) && ( ! force_acceleration_off )) { + //Is running is determined when a buffer item is added, however + //if st_interrupt deletes a buffer item, then is_running must have changed + //and now be false + if ( st_interrupt() ) is_running = false; } else { #endif if (is_running) { @@ -444,7 +1090,6 @@ bool doInterrupt() { axes[i].doInterrupt(intervals); } } - return is_running; } else if (is_homing) { is_homing = false; for (int i = 0; i < STEPPER_COUNT; i++) { @@ -454,25 +1099,31 @@ bool doInterrupt() { #ifdef HAS_STEPPER_ACCELERATION //If homing has finished and we're accelerated, switch back to the accelerated drived - if (( ! is_homing ) && ( acceleration )) switchToAcceleratedDriver(); + if (( ! is_homing ) && ( acceleration ) && ( force_acceleration_off )) switchToAcceleratedDriver(); #endif - - return is_homing; } #ifdef HAS_STEPPER_ACCELERATION } #endif - return false; + +#if defined(DEBUG_ZADVANCE) && defined(TIME_STEPPER_INTERRUPT) + DEBUG_TIMER_FINISH; + debugTimer = DEBUG_TIMER_TCTIMER_USI; +#endif } #ifdef HAS_STEPPER_ACCELERATION bool doAdvanceInterrupt() { -#ifdef ADVANCE +#ifdef JKN_ADVANCE if ( acceleration ) st_advance_interrupt(); #endif } +void setSegmentAccelState(bool state) { + segmentAccelState = state; +} + #endif } diff --git a/firmware/src/Motherboard/Steppers.hh b/firmware/src/Motherboard/Steppers.hh index d6c3b1d..fce73f7 100644 --- a/firmware/src/Motherboard/Steppers.hh +++ b/firmware/src/Motherboard/Steppers.hh @@ -88,25 +88,45 @@ namespace steppers { void definePosition(const Point& position); /// Switch to the regular driver - void switchToRegularDriver(); + /// If lockout = true, acceleration can't be autoswitched back on extrusion until + /// switchToAcceleratedDriver has been called + void switchToRegularDriver(bool lockout); ///Switch to the accelerated driver void switchToAcceleratedDriver(); -//Debugging -void doLcd(); + ///Returns true if we're current running accelerated + bool isAccelerated(void); + + /// Toggle segment acceleration on or off + void setSegmentAccelState(bool state); + +#ifdef DEBUG_ZADVANCE + void doLcd(); +#endif + + //Run the stepper slice + void runSteppersSlice(); /// Handle interrupt. - /// \return True if the stepper subsystem is currently in motion. - bool doInterrupt(); + void doInterrupt(); - //Used for the ADVANCE algorithm in Marlin + //Used for the JKN_ADVANCE algorithm bool doAdvanceInterrupt(); - /// Get the current system position - /// \return The current machine position. + /// Get position + /// When accelerated, this is the target position of the command at the end of the pipeline const Point getPosition(); + /// Get current position + /// When accelerated, this is the position right now + const Point getCurrentPosition(); + +#ifdef HAS_STEPPER_ACCELERATION + /// Drains the acceleration buffer to empty + void drainAccelerationBuffer(void); +#endif + /// Control whether the Z axis should stay enabled during the entire /// build (defaults to off). This is useful for machines that have /// a z-axis that might slip if the motor does not stay enagaged. diff --git a/firmware/src/Motherboard/boards/mb24/Configuration.hh b/firmware/src/Motherboard/boards/mb24/Configuration.hh index ced7ede..dbb8e81 100644 --- a/firmware/src/Motherboard/boards/mb24/Configuration.hh +++ b/firmware/src/Motherboard/boards/mb24/Configuration.hh @@ -196,4 +196,21 @@ //Stepper Acceleration #define HAS_STEPPER_ACCELERATION 1 +//Build estimation for the lcd +#define HAS_BUILD_ESTIMATION 1 + +//Pause@ZPos functionality for the LCD +#define PAUSEATZPOS + +//Filament counter +#define HAS_FILAMENT_COUNTER + +//If defined, erase the eeprom area on every boot, useful for diagnostics +//#define ERASE_EEPROM_ON_EVERY_BOOT + +//If defined, enable an additional Utilities menu that allows erasing, saving and loading +//of eeprom data +//WARNING: This probably will not fit on a 1280 (mb24). +//#define EEPROM_MENU_ENABLE + #endif // BOARDS_RRMBV12_CONFIGURATION_HH_ diff --git a/firmware/src/Motherboard/boards/mb24/EepromDefaults.hh b/firmware/src/Motherboard/boards/mb24/EepromDefaults.hh new file mode 100644 index 0000000..b0e3443 --- /dev/null +++ b/firmware/src/Motherboard/boards/mb24/EepromDefaults.hh @@ -0,0 +1,110 @@ +// +// MBI Firmware Defaults +// +#define EEPROM_DEFAULT_AXIS_INVERSION (uint8_t)(1<<1) // Y axis = 1 +#define EEPROM_DEFAULT_ENDSTOP_INVERSION (uint8_t)0b00011111 // All endstops inverted + +#define EEPROM_DEFAULT_MACHINE_NAME 0 // name is null + +#define EEPROM_DEFAULT_ESTOP_CONFIGURATION eeprom::ESTOP_CONF_NONE + +// Not strictly MBI defaults, but we but them here as we never want these values to be overwritten +#define EEPROM_DEFAULT_FILAMENT_USED 0 +#define EEPROM_DEFAULT_FILAMENT_USED_TRIP 0 + + + + +// +// Jetty Firmware Defaults +// +#define EEPROM_DEFAULT_TOOL0_TEMP 220 +#define EEPROM_DEFAULT_TOOL1_TEMP 220 +#define EEPROM_DEFAULT_PLATFORM_TEMP 110 + +#define EEPROM_DEFAULT_EXTRUDE_DURATION 1 +#define EEPROM_DEFAULT_EXTRUDE_MMS 10 + +#define EEPROM_DEFAULT_MOOD_LIGHT_SCRIPT 0 +#define EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_RED 255 +#define EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_GREEN 255 +#define EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_BLUE 255 + +#define EEPROM_DEFAULT_JOG_MODE_SETTINGS 0 + +#define EEPROM_DEFAULT_BUZZER_REPEATS 3 + +// The next 4 entries, aren't really the defaults, but they're used in EEPROM_DEFAULT_STEPS_PER_MM_?, so we include +// them here so we don't miss them if we change scaling +#define STEPS_PER_MM_PADDING 5 +#define STEPS_PER_MM_PRECISION 10 +#define STEPS_PER_MM_LOWER_LIMIT 10000000 +#define STEPS_PER_MM_UPPER_LIMIT 200000000000000 + +#define EEPROM_DEFAULT_STEPS_PER_MM_X 470698520000 // 47.069852 +#define EEPROM_DEFAULT_STEPS_PER_MM_Y 470698520000 // 47.069852 +#define EEPROM_DEFAULT_STEPS_PER_MM_Z 2000000000000 // 200.0 +#define EEPROM_DEFAULT_STEPS_PER_MM_A 502354788069 // 50.2354788069 +#define EEPROM_DEFAULT_STEPS_PER_MM_B 502354788069 // 50.2354788069 + +#define EEPROM_DEFAULT_ABP_COPIES 1 + +#define EEPROM_DEFAULT_PREHEAT_DURING_ESTIMATE 0 + +#define EEPROM_DEFAULT_OVERRIDE_GCODE_TEMP 0 + +#define EEPROM_DEFAULT_STEPPER_DRIVER 0x03 + +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Z 16 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_A 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_B 100 + +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_X 500 +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Y 500 +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Z 150 +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_A 60000 + +#define EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_NORM 2000 +#define EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_RETRACT 4000 + +#define EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM 44 // Multiplied by 10 + +#define EEPROM_DEFAULT_ACCEL_MIN_FEED_RATE 0 // Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MIN_TRAVEL_FEED_RATE 0 // Multiplied by 10 + +#define EEPROM_DEFAULT_ACCEL_ADVANCE_K 850 // 0.00850 Multiplied by 100000 +#define EEPROM_DEFAULT_ACCEL_ADVANCE_K2 900 // 0.00900 Multiplied by 100000 + +#define EEPROM_DEFAULT_ACCEL_MIN_PLANNER_SPEED 2 // mm/s + +#define EEPROM_DEFAULT_ACCEL_NOODLE_DIAMETER 58 // 0.58mm Multiplied by 100 + +#define EEPROM_DEFAULT_ACCEL_MIN_SEGMENT_TIME 200 // 0.0200 Multiplied by 10000 + +#define LCD_TYPE_16x4 0 +#define LCD_TYPE_20x4 50 +#define LCD_TYPE_24x4 51 +#define EEPROM_DEFAULT_LCD_TYPE LCD_TYPE_16x4 // 20x4 = 50, 24x4 = 51, anything else = 16x4 (done that way to make it unlike anything other than + // 16x4 will get chosen due to corrupted eeprom +#define EEPROM_DEFAULT_ENDSTOPS_USED (32+4+1) // ZMax + YMin + XMin + +#define EEPROM_DEFAULT_HOMING_FEED_RATE_X 500 +#define EEPROM_DEFAULT_HOMING_FEED_RATE_Y 500 +#define EEPROM_DEFAULT_HOMING_FEED_RATE_Z 500 + +#define EEPROM_DEFAULT_ACCEL_REV_MAX_FEED_RATE 800 + +#define EEPROM_DEFAULT_ACCEL_EXTRUDER_DEPRIME 40 // 4mm Multiplied by 10 + +#define EEPROM_DEFAULT_ACCEL_SLOWDOWN_LIMIT 4 + +#define EEPROM_DEFAULT_ACCEL_CLOCKWISE_EXTRUDER 1 + +#define EEPROM_DEFAULT_INVERTED_EXTRUDER_5D 0 + +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_X 300 // mm/s Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Y 300 // mm/s Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Z 100 // mm/s Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_A 300 // mm/s Multiplied by 10 diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.cc b/firmware/src/Motherboard/boards/mb24/Motherboard.cc index f991ada..e15e4a3 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.cc +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.cc @@ -28,7 +28,9 @@ #include "Commands.hh" #include "Eeprom.hh" #include "EepromMap.hh" +#include "EepromDefaults.hh" #include +#include "StepperAccelPlanner.hh" /// Instantiate static motherboard instance Motherboard Motherboard::motherboard; @@ -103,20 +105,24 @@ void Motherboard::setupFixedStepperTimer() { } void Motherboard::setupAccelStepperTimer() { - // waveform generation = 0100 = CTC - TCCR1B &= ~(1< 1000000L) { seconds += 1; countupMicros -= 1000000L; @@ -289,11 +300,6 @@ ISR(TIMER3_COMPA_vect) { Motherboard::getBoard().doInterfaceInterrupt(); } -/// Timer one comparator match interrupt -ISR(TIMER4_COMPA_vect) { - Motherboard::getBoard().doAdvanceInterrupt(); -} - /// Number of times to blink the debug LED on each cycle volatile uint8_t blink_count = 0; @@ -429,8 +435,21 @@ int blink_ovfs_remaining = 0; /// Number of blinks performed in the current cycle int blinked_so_far = 0; -/// Timer 2 overflow interrupt -ISR(TIMER2_OVF_vect) { +int debug_light_interrupt_divisor = 0; +#define MAX_DEBUG_LIGHT_INTERRUPT_DIVISOR 164 //Timer interrupt frequency / (16MHz / 1026 / 256) + +/// Timer 2 comparator match interrupt +ISR(TIMER2_COMPA_vect) { +#if defined(JKN_ADVANCE) && defined(HAS_STEPPER_ACCELERATION) + Motherboard::getBoard().doAdvanceInterrupt(); +#endif + + debug_light_interrupt_divisor ++; + if ( debug_light_interrupt_divisor < MAX_DEBUG_LIGHT_INTERRUPT_DIVISOR ) + return; + + debug_light_interrupt_divisor = 0; + if (blink_ovfs_remaining > 0) { blink_ovfs_remaining--; } else { diff --git a/firmware/src/Motherboard/boards/mb24/Motherboard.hh b/firmware/src/Motherboard/boards/mb24/Motherboard.hh index 8f48c4a..2fa3c9e 100644 --- a/firmware/src/Motherboard/boards/mb24/Motherboard.hh +++ b/firmware/src/Motherboard/boards/mb24/Motherboard.hh @@ -44,6 +44,7 @@ private: public: /// Get the motherboard instance. static Motherboard& getBoard() { return motherboard; } + LiquidCrystal lcd; private: /// Collection of stepper controllers that are on this board @@ -64,7 +65,6 @@ private: bool hasInterfaceBoard; ButtonArray buttonArray; - LiquidCrystal lcd; InterfaceBoard interfaceBoard; MoodLightController moodLightController; @@ -99,6 +99,9 @@ public: void setupFixedStepperTimer(); void setupAccelStepperTimer(); + //Enable / Disable Timer Interrupts + void enableTimerInterrupts(bool enable); + /// Reset the motherboard to its initial state. /// This only resets the board, and does not send a reset /// to any attached toolheads. diff --git a/firmware/src/Motherboard/boards/rrmbv12-2560/Configuration.hh b/firmware/src/Motherboard/boards/rrmbv12-2560/Configuration.hh new file mode 100644 index 0000000..b273e50 --- /dev/null +++ b/firmware/src/Motherboard/boards/rrmbv12-2560/Configuration.hh @@ -0,0 +1,173 @@ +/* + * Copyright 2010 by Adam Mayer + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef BOARDS_RRMBV12_CONFIGURATION_HH_ +#define BOARDS_RRMBV12_CONFIGURATION_HH_ + +// This file details the pin assignments and features of the RepRap Motherboard +// version 1.2 for the ordinary use case. + + +// Interval for the stepper update in microseconds. This interval is the minimum +// possible time between steps; in practical terms, your time between steps should +// be at least eight times this large. Reducing the interval can cause resource +// starvation; leave this at 64uS or greater unless you know what you're doing. +#define INTERVAL_IN_MICROSECONDS 128 + +// The pin that connects to the /PS_ON pin on the PSU header. This pin switches +// on the PSU when pulled low. +#define PSU_PIN Pin(PortD,6) + +// --- Secure Digital Card configuration --- +// NOTE: If SD support is enabled, it is implicitly assumed that the +// following pins are connected: +// AVR | SD header +//---------|-------------- +// MISO | DATA_OUT +// MOSI | DATA_IN +// SCK | CLK + +// Define as 1 if and SD card slot is present; 0 if not. +#define HAS_SD 1 +// The pin that connects to the write protect line on the SD header. +#define SD_WRITE_PIN Pin(PortD,0) +// The pin that connects to the card detect line on the SD header. +#define SD_DETECT_PIN Pin(PortD,1) +// The pin that connects to the chip select line on the SD header. +#define SD_SELECT_PIN Pin(PortB,0) + +// --- Slave UART configuration --- +// The slave UART is presumed to be an RS485 connection through a sn75176 chip. +// Define as 1 if the slave UART is present; 0 if not. +#define HAS_SLAVE_UART 1 +// The pin that connects to the driver enable line on the RS485 chip. +#define TX_ENABLE_PIN Pin(PortC,5) +// The pin that connects to the active-low recieve enable line on the RS485 chip. +#define RX_ENABLE_PIN Pin(PortC,7) + +// --- Host UART configuration --- +// The host UART is presumed to always be present on the RX/TX lines. + +// --- Axis configuration --- +// Define the number of stepper axes supported by the board. The axes are +// denoted by X, Y, Z, A and B. +#ifndef FOURTH_STEPPER + // Ordinary G3 motherboards have three stepper terminals. + #define STEPPER_COUNT 3 +#else + // Rob G's hacked G3 motherboard supports four steppers. + #define STEPPER_COUNT 4 +#endif // FOURTH_STEPPER + +// --- Stepper and endstop configuration --- +// Pins should be defined for each axis present on the board. They are denoted +// X, Y, Z, A and B respectively. + +// This indicates the default interpretation of the endstop values. +// If your endstops are based on the H21LOB, they are inverted; +// if they are based on the H21LOI, they are not. +#define DEFAULT_INVERTED_ENDSTOPS 1 + +#ifdef FOURTH_STEPPER + // If both ends of the endstops will trigger the same pin, set this to one. + // The hacked G3 four-stepper shield does this to conserve pins. + #define SINGLE_SWITCH_ENDSTOPS 1 +#endif + +// The X stepper step pin (active on rising edge) +#define X_STEP_PIN Pin(PortA,6) +// The X direction pin (forward on logic high) +#define X_DIR_PIN Pin(PortA,5) +// The X stepper enable pin (active low) +#define X_ENABLE_PIN Pin(PortA,4) +// The X minimum endstop pin (active high) +#define X_MIN_PIN Pin(PortB,6) +// The X maximum endstop pin (active high) +#if defined(SINGLE_SWITCH_ENDSTOPS) && (SINGLE_SWITCH_ENDSTOPS == 1) + #define X_MAX_PIN Pin(PortB,5) +#else + #define X_MAX_PIN Pin(PortB,5) +#endif + +// The Y stepper step pin (active on rising edge) +#define Y_STEP_PIN Pin(PortA,3) +// The Y direction pin (forward on logic high) +#define Y_DIR_PIN Pin(PortA,2) +// The Y stepper enable pin (active low) +#define Y_ENABLE_PIN Pin(PortA,1) +// The Y minimum endstop pin (active high) +#define Y_MIN_PIN Pin(PortB,4) +// The Y maximum endstop pin (active high) +#if defined(SINGLE_SWITCH_ENDSTOPS) && (SINGLE_SWITCH_ENDSTOPS == 1) + #define Y_MAX_PIN Pin(PortH,6) +#else + #define Y_MAX_PIN Pin(PortH,6) +#endif + +// The Z stepper step pin (active on rising edge) +#define Z_STEP_PIN Pin(PortA,0) +// The Z direction pin (forward on logic high) +#define Z_DIR_PIN Pin(PortH,0) +// The Z stepper enable pin (active low) +#define Z_ENABLE_PIN Pin(PortH,1) +// The Z minimum endstop pin (active high) +#define Z_MIN_PIN Pin(PortH,5) +// The Z maximum endstop pin (active high) +#if defined(SINGLE_SWITCH_ENDSTOPS) && (SINGLE_SWITCH_ENDSTOPS == 1) + #define Z_MAX_PIN Pin(PortH,4) +#else + #define Z_MAX_PIN Pin(PortH,4) +#endif + +#ifdef FOURTH_STEPPER + // The A stepper step pin (active on rising edge) + #define A_STEP_PIN Pin(PortJ,0) + // The A direction pin (forward on logic high) + #define A_DIR_PIN Pin(PortJ,1) + // The A stepper enable pin (active low) + #define A_ENABLE_PIN Pin(PortE,5) +#endif // FOURTH_STEPPER + + +// --- Debugging configuration --- +// The pin which controls the debug LED (active high) +#define DEBUG_PIN Pin(PortB,7) + +// By default, debugging packets should be honored; this is made +// configurable if we're short on cycles or EEPROM. +// Define as 1 if debugging packets are honored; 0 if not. +#define HONOR_DEBUG_PACKETS 1 + +//Stepper Acceleration +#define HAS_STEPPER_ACCELERATION 1 + +#define INTERFACE_FOO_PIN Pin(PortC,0) + +//If defined, erase the eeprom area on every boot, useful for diagnostics +//#define ERASE_EEPROM_ON_EVERY_BOOT + +//If defined, overrides UART to run at 38400 instead of 115200 (the default for the Mega) +#define OVERRIDE_UART_TO_38400_BAUD + +//Stores the ram usage to eeprom everytime switchToRegularDriver is called +//You don't want to leave this enabled as it can cause excessive eeprom write +//#define STORE_RAM_USAGE_TO_EEPROM + +//Defined on Atmega644p, indicates a 4K instead of 8K ram +#define SMALL_4K_RAM + +#endif // BOARDS_RRMBV12_CONFIGURATION_HH_ diff --git a/firmware/src/Motherboard/boards/rrmbv12-2560/EepromDefaults.hh b/firmware/src/Motherboard/boards/rrmbv12-2560/EepromDefaults.hh new file mode 100644 index 0000000..df6b5af --- /dev/null +++ b/firmware/src/Motherboard/boards/rrmbv12-2560/EepromDefaults.hh @@ -0,0 +1,75 @@ +// +// MBI Firmware Defaults +// +#define EEPROM_DEFAULT_AXIS_INVERSION (uint8_t)(1<<1) // Y axis = 1 +#define EEPROM_DEFAULT_ENDSTOP_INVERSION (uint8_t)0b00011111 // All endstops inverted + +#define EEPROM_DEFAULT_MACHINE_NAME 0 // name is null + +// Not strictly MBI defaults, but we but them here as we never want these values to be overwritten +#define EEPROM_DEFAULT_FILAMENT_USED 0 +#define EEPROM_DEFAULT_FILAMENT_USED_TRIP 0 + + + + +// +// Jetty Firmware Defaults +// +#define EEPROM_DEFAULT_JOG_MODE_SETTINGS 0 + +// The next 4 entries, aren't really the defaults, but they're used in EEPROM_DEFAULT_STEPS_PER_MM_?, so we include +// them here so we don't miss them if we change scaling +#define STEPS_PER_MM_PADDING 5 +#define STEPS_PER_MM_PRECISION 10 +#define STEPS_PER_MM_LOWER_LIMIT 10000000 +#define STEPS_PER_MM_UPPER_LIMIT 200000000000000 + +#define EEPROM_DEFAULT_STEPS_PER_MM_X 117674630000 // 11.767463 +#define EEPROM_DEFAULT_STEPS_PER_MM_Y 117674630000 // 11.767463 +#define EEPROM_DEFAULT_STEPS_PER_MM_Z 3200000000000 // 320.0 +#define EEPROM_DEFAULT_STEPS_PER_MM_A 502354788069 // 50.2354788069 +#define EEPROM_DEFAULT_STEPS_PER_MM_B 502354788069 // 50.2354788069 + +#define EEPROM_DEFAULT_STEPPER_DRIVER 0x03 + +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Z 8 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_A 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_B 100 + +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_X 500 +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Y 500 +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Z 100 +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_A 60000 + +#define EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_NORM 2000 +#define EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_RETRACT 4000 + +#define EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM 44 // Multiplied by 10 + +#define EEPROM_DEFAULT_ACCEL_MIN_FEED_RATE 0 // Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MIN_TRAVEL_FEED_RATE 0 // Multiplied by 10 + +#define EEPROM_DEFAULT_ACCEL_ADVANCE_K 850 // 0.00850 Multiplied by 100000 +#define EEPROM_DEFAULT_ACCEL_ADVANCE_K2 900 // 0.00900 Multiplied by 100000 + +#define EEPROM_DEFAULT_ACCEL_MIN_PLANNER_SPEED 2 // mm/s + +#define EEPROM_DEFAULT_ACCEL_NOODLE_DIAMETER 58 // 0.58mm Multiplied by 100 + +#define EEPROM_DEFAULT_ACCEL_MIN_SEGMENT_TIME 200 // 0.0200 Multiplied by 10000 + +#define EEPROM_DEFAULT_ACCEL_REV_MAX_FEED_RATE 800 + +#define EEPROM_DEFAULT_ACCEL_EXTRUDER_DEPRIME 40 // 4mm Multiplied by 10 + +#define EEPROM_DEFAULT_ACCEL_SLOWDOWN_LIMIT 4 + +#define EEPROM_DEFAULT_ACCEL_CLOCKWISE_EXTRUDER 1 + +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_X 300 // mm/s Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Y 300 // mm/s Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Z 25 // mm/s Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_A 300 // mm/s Multiplied by 10 diff --git a/firmware/src/Motherboard/boards/rrmbv12-2560/Motherboard.cc b/firmware/src/Motherboard/boards/rrmbv12-2560/Motherboard.cc new file mode 100644 index 0000000..5f33fff --- /dev/null +++ b/firmware/src/Motherboard/boards/rrmbv12-2560/Motherboard.cc @@ -0,0 +1,285 @@ +/* + * Copyright 2010 by Adam Mayer + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + +#include +#include +#include +#include +#include "Motherboard.hh" +#include "Configuration.hh" +#include "Steppers.hh" +#include "Command.hh" +#include "Eeprom.hh" +#include "EepromMap.hh" +#include "EepromDefaults.hh" +#include "StepperAccelPlanner.hh" + +/// Instantiate static motherboard instance +Motherboard Motherboard::motherboard(PSU_PIN); + +/// Create motherboard object +Motherboard::Motherboard(const Pin& psu_pin) : + psu(psu_pin) +{ + /// Set up the stepper pins on board creation +#if STEPPER_COUNT > 0 + stepper[0] = StepperInterface(X_DIR_PIN, + X_STEP_PIN, + X_ENABLE_PIN, + X_MAX_PIN, + X_MIN_PIN, + eeprom::AXIS_INVERSION); +#endif +#if STEPPER_COUNT > 1 + stepper[1] = StepperInterface(Y_DIR_PIN, + Y_STEP_PIN, + Y_ENABLE_PIN, + Y_MAX_PIN, + Y_MIN_PIN, + eeprom::AXIS_INVERSION); +#endif +#if STEPPER_COUNT > 2 + stepper[2] = StepperInterface(Z_DIR_PIN, + Z_STEP_PIN, + Z_ENABLE_PIN, + Z_MAX_PIN, + Z_MIN_PIN, + eeprom::AXIS_INVERSION); +#endif +#if STEPPER_COUNT > 3 + stepper[3] = StepperInterface(A_DIR_PIN, + A_STEP_PIN, + A_ENABLE_PIN, + Pin(), + Pin(), + eeprom::AXIS_INVERSION); +#endif +} + +void Motherboard::setupFixedStepperTimer() { + TCCR1A = 0x00; + TCCR1B = 0x09; + TCCR1C = 0x00; + OCR1A = INTERVAL_IN_MICROSECONDS * 16; + TIMSK1 = 0x02; // turn on OCR1A match interrupt +} + +void Motherboard::setupAccelStepperTimer() { + TCCR1A = 0x00; + TCCR1B = 0x0A; //CTC1 + / 8 = 2Mhz. + TCCR1C = 0x00; + OCR1A = 0x2000; //1KHz + TIMSK1 = 0x02; // turn on OCR1A match interrupt +} + +void Motherboard::enableTimerInterrupts(bool enable) { + if ( enable ) { + TIMSK1 |= (1< 0) { + blink_ovfs_remaining--; + } else { + if (blink_state == BLINK_ON) { + blinked_so_far++; + blink_state = BLINK_OFF; + blink_ovfs_remaining = OVFS_OFF; + DEBUG_PIN.setValue(false); + } else if (blink_state == BLINK_OFF) { + if (blinked_so_far >= blink_count) { + blink_state = BLINK_PAUSE; + blink_ovfs_remaining = OVFS_PAUSE; + } else { + blink_state = BLINK_ON; + blink_ovfs_remaining = OVFS_ON; + DEBUG_PIN.setValue(true); + } + } else if (blink_state == BLINK_PAUSE) { + blinked_so_far = 0; + blink_state = BLINK_ON; + blink_ovfs_remaining = OVFS_ON; + DEBUG_PIN.setValue(true); + } + } +} diff --git a/firmware/src/Motherboard/boards/rrmbv12-2560/Motherboard.hh b/firmware/src/Motherboard/boards/rrmbv12-2560/Motherboard.hh new file mode 100644 index 0000000..3927c99 --- /dev/null +++ b/firmware/src/Motherboard/boards/rrmbv12-2560/Motherboard.hh @@ -0,0 +1,98 @@ +/* + * Copyright 2010 by Adam Mayer + * + * 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, or + * (at your option) any later version. + * + * 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 + */ + +#ifndef BOARDS_RRMBV12_MOTHERBOARD_HH_ +#define BOARDS_RRMBV12_MOTHERBOARD_HH_ + + +#include "UART.hh" +#include "StepperInterface.hh" +#include "Types.hh" +#include "PSU.hh" +#include "Configuration.hh" + +/// Main class for Motherboard version 1.2 (Gen3 electronics) +/// \ingroup HardwareLibraries +/// \ingroup MBv12 +class Motherboard { +private: + const static int STEPPERS = STEPPER_COUNT; + + StepperInterface stepper[STEPPERS]; + + PSU psu; + + /// Microseconds since board initialization + volatile micros_t micros; + + /// Private constructor; use the singleton + Motherboard(const Pin& psu_pin); + + static Motherboard motherboard; +public: + //2 types of stepper timers depending on if we're using accelerated or not + void setupFixedStepperTimer(); + void setupAccelStepperTimer(); + + //Enable / Disable Timer Interrupts + void enableTimerInterrupts(bool enable); + + /// Reset the motherboard to its initial state. + /// This only resets the board, and does not send a reset + /// to any attached toolheads. + void reset(bool hard_reset); + + void runMotherboardSlice(); + + /// Count the number of steppers available on this board. + const int getStepperCount() const { return STEPPERS; } + + /// Get the stepper interface for the nth stepper. + StepperInterface& getStepperInterface(int n) + { + return stepper[n]; + } + + StepperInterface *getStepperAllInterfaces() + { + return stepper; + } + + /// Get the number of microseconds that have passed since + /// the board was initialized. This value will wrap after + /// 2**16 microseconds; callers should compensate for this. + micros_t getCurrentMicros(); + void resetCurrentSeconds(); + + /// Write an error code to the debug pin. + void indicateError(int errorCode); + + /// Get the current error being displayed. + uint8_t getCurrentError(); + + /// Get the motherboard instance. + static Motherboard& getBoard() { return motherboard; } + + /// Perform the stepper timer interrupt routine. + void doStepperInterrupt(); + + void doAdvanceInterrupt(); + + void updateMicros(); +}; + +#endif // BOARDS_RRMBV12_MOTHERBOARD_HH_ diff --git a/firmware/src/Motherboard/boards/rrmbv12/Configuration.hh b/firmware/src/Motherboard/boards/rrmbv12/Configuration.hh index a538829..cb39974 100644 --- a/firmware/src/Motherboard/boards/rrmbv12/Configuration.hh +++ b/firmware/src/Motherboard/boards/rrmbv12/Configuration.hh @@ -26,7 +26,7 @@ // possible time between steps; in practical terms, your time between steps should // be at least eight times this large. Reducing the interval can cause resource // starvation; leave this at 64uS or greater unless you know what you're doing. -#define INTERVAL_IN_MICROSECONDS 64 +#define INTERVAL_IN_MICROSECONDS 128 // The pin that connects to the /PS_ON pin on the PSU header. This pin switches // on the PSU when pulled low. @@ -152,4 +152,13 @@ // Define as 1 if debugging packets are honored; 0 if not. #define HONOR_DEBUG_PACKETS 1 +//Stepper Acceleration +#define HAS_STEPPER_ACCELERATION 1 + +//If defined, erase the eeprom area on every boot, useful for diagnostics +//#define ERASE_EEPROM_ON_EVERY_BOOT + +//Defined on Atmega644p, indicates a 4K instead of 8K ram +#define SMALL_4K_RAM + #endif // BOARDS_RRMBV12_CONFIGURATION_HH_ diff --git a/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh b/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh new file mode 100644 index 0000000..556587e --- /dev/null +++ b/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh @@ -0,0 +1,78 @@ +// +// MBI Firmware Defaults +// +#define EEPROM_DEFAULT_AXIS_INVERSION (uint8_t)(1<<1) // Y axis = 1 +#define EEPROM_DEFAULT_ENDSTOP_INVERSION (uint8_t)0b00011111 // All endstops inverted + +#define EEPROM_DEFAULT_MACHINE_NAME 0 // name is null + +// Not strictly MBI defaults, but we but them here as we never want these values to be overwritten +#define EEPROM_DEFAULT_FILAMENT_USED 0 +#define EEPROM_DEFAULT_FILAMENT_USED_TRIP 0 + + + + +// +// Jetty Firmware Defaults +// +#define EEPROM_DEFAULT_JOG_MODE_SETTINGS 0 + +// The next 4 entries, aren't really the defaults, but they're used in EEPROM_DEFAULT_STEPS_PER_MM_?, so we include +// them here so we don't miss them if we change scaling +#define STEPS_PER_MM_PADDING 5 +#define STEPS_PER_MM_PRECISION 10 +#define STEPS_PER_MM_LOWER_LIMIT 10000000 +#define STEPS_PER_MM_UPPER_LIMIT 200000000000000 + +// These defaults are appropriate for a Cupcake with the original stepper driver and +// a Mk7, Mk6+, or Mk5+Mk6 upgrade + +#define EEPROM_DEFAULT_STEPS_PER_MM_X 117674630000 // 11.767463 +#define EEPROM_DEFAULT_STEPS_PER_MM_Y 117674630000 // 11.767463 +#define EEPROM_DEFAULT_STEPS_PER_MM_Z 3200000000000 // 320.0 +#define EEPROM_DEFAULT_STEPS_PER_MM_A 502354788069 // 50.2354788069 +#define EEPROM_DEFAULT_STEPS_PER_MM_B 502354788069 // 50.2354788069 + +#define EEPROM_DEFAULT_STEPPER_DRIVER 0x03 + +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Z 8 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_A 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_B 100 + +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_X 500 +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Y 500 +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Z 100 +#define EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_A 60000 + +#define EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_NORM 2000 +#define EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_RETRACT 4000 + +#define EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM 44 // Multiplied by 10 + +#define EEPROM_DEFAULT_ACCEL_MIN_FEED_RATE 0 // Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MIN_TRAVEL_FEED_RATE 0 // Multiplied by 10 + +#define EEPROM_DEFAULT_ACCEL_ADVANCE_K 850 // 0.00850 Multiplied by 100000 +#define EEPROM_DEFAULT_ACCEL_ADVANCE_K2 900 // 0.00900 Multiplied by 100000 + +#define EEPROM_DEFAULT_ACCEL_MIN_PLANNER_SPEED 2 // mm/s + +#define EEPROM_DEFAULT_ACCEL_NOODLE_DIAMETER 58 // 0.58mm Multiplied by 100 + +#define EEPROM_DEFAULT_ACCEL_MIN_SEGMENT_TIME 200 // 0.0200 Multiplied by 10000 + +#define EEPROM_DEFAULT_ACCEL_REV_MAX_FEED_RATE 800 + +#define EEPROM_DEFAULT_ACCEL_EXTRUDER_DEPRIME 40 // 4mm Multiplied by 10 + +#define EEPROM_DEFAULT_ACCEL_SLOWDOWN_LIMIT 4 + +#define EEPROM_DEFAULT_ACCEL_CLOCKWISE_EXTRUDER 1 + +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_X 300 // mm/s Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Y 300 // mm/s Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Z 25 // mm/s Multiplied by 10 +#define EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_A 300 // mm/s Multiplied by 10 diff --git a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc index a5fdc45..57e0f33 100644 --- a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc +++ b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.cc @@ -25,6 +25,8 @@ #include "Command.hh" #include "Eeprom.hh" #include "EepromMap.hh" +#include "EepromDefaults.hh" +#include "StepperAccelPlanner.hh" /// Instantiate static motherboard instance Motherboard Motherboard::motherboard(PSU_PIN); @@ -66,14 +68,33 @@ Motherboard::Motherboard(const Pin& psu_pin) : Pin(), eeprom::AXIS_INVERSION); #endif -#if STEPPER_COUNT > 4 - stepper[4] = StepperInterface(B_DIR_PIN, - B_STEP_PIN, - B_ENABLE_PIN, - Pin(), - Pin(), - eeprom::AXIS_INVERSION); -#endif +} + +void Motherboard::setupFixedStepperTimer() { + TCCR1A = 0x00; + TCCR1B = 0x09; + TCCR1C = 0x00; + OCR1A = INTERVAL_IN_MICROSECONDS * 16; + TIMSK1 = 0x02; // turn on OCR1A match interrupt +} + +void Motherboard::setupAccelStepperTimer() { + TCCR1A = 0x00; + TCCR1B = 0x0A; //CTC1 + / 8 = 2Mhz. + TCCR1C = 0x00; + OCR1A = 0x2000; //1KHz + TIMSK1 = 0x02; // turn on OCR1A match interrupt +} + +void Motherboard::enableTimerInterrupts(bool enable) { + if ( enable ) { + TIMSK1 |= (1< 0) { blink_ovfs_remaining--; } else { diff --git a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh index 0c5f088..3927c99 100644 --- a/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh +++ b/firmware/src/Motherboard/boards/rrmbv12/Motherboard.hh @@ -44,6 +44,13 @@ private: static Motherboard motherboard; public: + //2 types of stepper timers depending on if we're using accelerated or not + void setupFixedStepperTimer(); + void setupAccelStepperTimer(); + + //Enable / Disable Timer Interrupts + void enableTimerInterrupts(bool enable); + /// Reset the motherboard to its initial state. /// This only resets the board, and does not send a reset /// to any attached toolheads. @@ -60,6 +67,11 @@ public: return stepper[n]; } + StepperInterface *getStepperAllInterfaces() + { + return stepper; + } + /// Get the number of microseconds that have passed since /// the board was initialized. This value will wrap after /// 2**16 microseconds; callers should compensate for this. @@ -75,8 +87,12 @@ public: /// Get the motherboard instance. static Motherboard& getBoard() { return motherboard; } - /// Perform the timer interrupt routine. - void doInterrupt(); + /// Perform the stepper timer interrupt routine. + void doStepperInterrupt(); + + void doAdvanceInterrupt(); + + void updateMicros(); }; #endif // BOARDS_RRMBV12_MOTHERBOARD_HH_ diff --git a/firmware/src/Motherboard/eeprom.py b/firmware/src/Motherboard/eeprom.py new file mode 100644 index 0000000..0e9fe2d --- /dev/null +++ b/firmware/src/Motherboard/eeprom.py @@ -0,0 +1,390 @@ +""" +Portions of this code is a copy of a work-in-progress from Makerbot Inc.'s s3g/ repo, + + https://github.com/makerbot/s3g/blob/master/makerbot_driver/EEPROM/eeprom_analyzer.py + +It then has been extended to write some useful Java code for the ReplicatorG source +files JettyEEPROM.java, OnboardParameters.java, and Makerbot4GDriver.java +""" + +""" +eeprom_analyzer.py with some slight changes starts here +""" + +""" +Analyzes the eeprom map in the Mightyboards Source code's +Eeprom.hh file. Expects blocks of information in the form of: +//$BEGIN_ENTRY +//$S: +const static uint16_t = + +Takes the above information and turns it into a python +dict in the form of: +{ + : { + size : , + address : , + } +} +""" + +class EndOfNamespaceError(IOError): + def __init__(self): + pass + +class EndOfEepromError(IOError): + def __init__(self): + pass + +class eeprom_analyzer(object): + + def __init__(self): + self.file = open('EepromMap.hh', 'r') + self.eeprom_map = {} + self.nl = {} + self.ln = {} + self.braces = 0 + + def parse_file(self): + self.eeprom_map = {} + try: + while True: + namespace_name = self.find_next_namespace().lower() + namespace = {} + try: + while True: + self.find_next_entry() + variables = self.parse_out_variables(self.file.readline()) + (name, location) = self.parse_out_name_and_location(self.file.readline()) + self.nl[name] = location + self.ln[location] = name + v = { + 'offset' : location, + } + for variable in variables: + variable = variable.split(':') + v[variable[0]] = variable[1] + namespace[name] = v + except EndOfNamespaceError: + self.eeprom_map[namespace_name] = namespace + except EndOfEepromError: + pass + + def find_next_entry(self): + namespace_end = '}' + open_brace = '{' + entry_line = '//$BEGIN_ENTRY' + line = self.file.readline() + while entry_line not in line: + if open_brace in line: + self.braces += 1 + if namespace_end in line: + self.braces -= 1 + if self.braces <= 0: + raise EndOfNamespaceError + line = self.file.readline() + return line + + def find_next_namespace(self): + """ + Reads lines from the file until a line that + starts with namespace is encountered. The name + of that namespace is then parsed out and returned. + + @return str name: The name of the current namespace + """ + namespace = 'namespace' + end_of_eeprom = '#endif' + line = self.file.readline() + while not line.startswith(namespace): + if end_of_eeprom in line: + raise EndOfEepromError + line = self.file.readline() + self.braces += 1 + return self.parse_out_namespace_name(line) + + def parse_out_namespace_name(self, line): + """ + Given a line in the form of + "namespace {" + parses out the name + + @param str line: The line with the namespace + @return str name: The name of the namespace + """ + line = line.strip() + for s in ['\n', '\r', '\t', '{']: + line = line.rstrip(s) + line = line.lstrip('namespace') + line = line.replace(' ', '') + return line + + def parse_out_name_and_location(self, line): + """ + Given a line in the form of: + const static uint16_t = ; + parses out the name and location. + If we get a line not of this form, we will fail. + + @param str line: the line we want information from + @return tuple: Information in the form of (name, location) + """ + for w in ['const', 'static', 'uint16_t']: + line = line.replace(w, '') + for s in ["\r", "\n", ";"]: + line = line.rstrip(s) + line = line.replace('\t', '') + line = line.replace(" ", "") + (name, location) = line.split("=") + return name, location + + def parse_out_variables(self, line): + line = line.strip() + for s in ['\n', '\r', '\t',]: + line = line.rstrip(s) + line = line.lstrip('//') + line = line.replace(' ', '') + parts = line.split('$') + #Dont return the first, since its empty + return parts[1:] + + def collate_maps(self, the_map): +# main_map = 'eeprom_offsets' +# collated_map = self.eeprom_map[main_map] + collated_map = the_map +# for key in self.eeprom_map[main_map]: + for key in the_map: +# if 'eeprom_map' in self.eeprom_map[main_map][key]: + if 'eeprom_map' in the_map[key]: +# sub_map_name = self.eeprom_map[main_map][key]['eeprom_map'] + sub_map_name = the_map[key]['eeprom_map'] + collated_map[key]['sub_map'] = self.eeprom_map[sub_map_name] + self.collate_maps(collated_map[key]['sub_map']) +# collated_map = {'eeprom_map' : collated_map} + return collated_map + +""" +eeprom_analyzer.py ends here +""" + +a = eeprom_analyzer() +a.parse_file() +offsets = a.eeprom_map['eeprom'] + +print "// EEPROM offsets for JettyEEPROM.java" +print "pacakge replicatorg.drivers.gen3;" +print +print "class JettyEEPROM extends Sanguino3GEEPRPOM {" +for offset in sorted(a.ln): + n = a.ln[offset] + print "\tfinal public static int %s = %s;" % (n.ljust(26), offset) +print "}" +print + +print "// EEPROMParams for OnboardParameters.java" +print "\tpublic enum EEPROMParams {" +for n in sorted(a.nl): + o = a.nl[n] + t = offsets[n]['type'] + if t == 'b': + tstr = 'int8_t' + elif t == 'B': + tstr = 'uint8_t' + elif t == 'I': + tstr = 'uint32_t' + elif t == 'i': + tstr = 'int32_t' + elif t == 'q': + tstr = 'int64_t' + elif t == 'Q': + tstr = 'uint64_t' + if 'exponent' in offsets[n]: + e = int(offsets[n]['exponent']) + if e < 0: + tstr += ' / 10^' + str(-e) + elif e > 0: + tstr += ' * 10^' + str(e) + print "\t\t%s// (%s, %s)" % ((n+',').ljust(28), tstr, o) +print "\t}" +print + +# Need a deep copy as we will be deleting members +nl = {} +for k in a.nl: + nl[k] = a.nl[k] + +print "\t@Override" +print "\tpublic int getEEPROMParamInt(EEPROMParams param) {" +print "\t\tswitch (param) {" +for name in sorted(nl): + d = offsets[name] + if not 'type' in d: + continue + if 'floating_point' in d: + continue + t = d['type'] + if t == 'B': + print "\t\tcase %s : return getUInt8EEPROM(JettyEEPROM.%s);" % (name.ljust(26), name) + del nl[name] + elif t == 'b': + print "\t\tcase %s : return getInt8EEPROM(JettyEEPROM.%s);" % (name.ljust(26), name) + del nl[name] + elif t == 'i': + print "\t\tcase %s : return getInt32EEPROM(JettyEEPROM.%s);" % (name.ljust(26), name) + del nl[name] +print "\t\tdefault :\n\t\t\tBase.logger.log(Level.DEBUG, \"getEEPROMParamInt(\" + param + \") call failed\");\n\t\t\treturn 0;" +print "\t\t}" +print "\t}" +print + +print "\t@Override" +print "\tpublic long getEEPROMParamUInt(EEPROMParams param) {" +print "\t\tswitch (param) {" +for name in sorted(nl): + d = offsets[name] + if not 'type' in d: + continue + if 'floating_point' in d: + continue + t = d['type'] + if t == 'I': + print "\t\tcase %s : return getUInt32EEPROM(JettyEEPROM.%s);" % (name.ljust(26), name) + del nl[name] +print "\t\tdefault :\n\t\t\tBase.logger.log(Level.DEBUG, \"getEEPROMParamUInt(\" + param + \") call failed\");\n\t\t\treturn 0L;" +print "\t\t}" +print "\t}" +print + +print "\t@Override" +print "\tpublic double getEEPROMParamFloat(EEPROMParams param) {" +print "\t\tswitch (param) {" +for name in sorted(nl): + d = offsets[name] + if not 'type' in d: + continue + if not ('floating_point' in d): + continue + if not 'exponent' in d: + continue + exp = int(d['exponent']) + s = '' + if exp < 0: + s = ' / %s.0d' % pow(10,-exp) + elif exp > 0: + s = ' * %s.0d' % pow(10, exp) + t = d['type'] + if t == 'B': + print "\t\tcase %s : return (double)getUInt8EEPROM(JettyEEPROM.%s)%s;" % (name.ljust(26), name, s) + del nl[name] + elif t == 'b': + print "\t\tcase %s : return (double)getInt8EEPROM(JettyEEPROM.%s)%s;" % (name.ljust(26), name, s) + del nl[name] + elif t == 'I': + print "\t\tcase %s : return (double)getUInt32EEPROM(JettyEEPROM.%s)%s;" % (name.ljust(26), name, s) + del nl[name] + elif t == 'i': + print "\t\tcase %s : return (double)getInt32EEPROM(JettyEEPROM.%s)%s;" % (name.ljust(26), name, s) + del nl[name] +print "\t\tdefault :\n\t\t\tBase.logger.log(Level.DEBUG, \"getEEPROMParamFloat(\" + param + \") call failed\");\n\t\t\treturn 0d;" +print "\t\t}" +print "\t}" +print "" + +for k in nl: + print "\t// Unhandled get:", k + +nl = {} +for k in a.nl: + nl[k] = a.nl[k] + +print "\t@Override" +print "\tpublic int setEEPROMParam(EEPROMParams param, int val) {" +print "\t\tif (val < 0)" +print "\t\t\tval = 0;" +print "\t\tswitch (param) {" +for name in sorted(nl): + d = offsets[name] + if not 'type' in d: + continue + if 'floating_point' in d: + continue + t = d['type'] + if t == 'B': + print "\t\tcase %s : setUInt8EEPROM(JettyEEPROM.%s, val); break;" % (name.ljust(26), name) + del nl[name] + elif t == 'b': + print "\t\tcase %s : setInt8EEPROM(JettyEEPROM.%s, val); break;" % (name.ljust(26), name) + del nl[name] + elif t == 'i': + print "\t\tcase %s : return setInt32EEPROM(JettyEEPROM.%s, val); break;" % (name.ljust(26), name) + del nl[name] +print "\t\tdefault : Base.logger.log(Level.DEBUG, \"setEEPROMParam(\" + param + \", \" + val + \") call failed\"); break;" +print "\t\t}" +print "\t}" +print "" + +print "\t@Override" +print "\tpublic long setEEPROMParam(EEPROMParams param, long val) {" +print "\t\tif (val < 0L)" +print "\t\t\tval = 0L;" +print "\t\tswitch (param) {" +for name in sorted(nl): + d = offsets[name] + if not 'type' in d: + continue + if 'floating_point' in d: + continue + t = d['type'] + if t == 'I': + print "\t\tcase %s : setUInt32EEPROM(JettyEEPROM.%s, val); break;" % (name.ljust(26), name) + del nl[name] +print "\t\tdefault : Base.logger.log(Level.DEBUG, \"setEEPROMParam(\" + param + \", \" + val + \") call failed\"); break;" +print "\t\t}" +print "\t}" +print "" + +print "\t@Override" +print "\tpublic double setEEPROMParam(EEPROMParams param, double val) {" +print "\t\tif (val < 0.0d)" +print "\t\t\tval = 0.0d;" +print "\t\tswitch (param) {" +for name in sorted(nl): + d = offsets[name] + if not 'type' in d: + continue + if not ('floating_point' in d): + continue + if not 'exponent' in d: + continue + exp = int(d['exponent']) + s = '' + sl = '' + sr = '' + if exp < 0: + sl = '(' + s = ' * %s.0d' % pow(10,-exp) + sr = ')' + elif exp > 0: + sl = '(' + s = ' / %s.0d' % pow(10, exp) + sr = ')' + t = d['type'] + if t == 'B': + print "\t\tcase %s : setUInt8EEPROM(JettyEEPROM.%s, (int)%sval%s%s); break;" % (name.ljust(26), name, sl, s, sr) + del nl[name] + elif t == 'b': + print "\t\tcase %s : setInt8EEPROM(JettyEEPROM.%s, (int)%sval%s%s); break;" % (name.ljust(26), name, sl, s, sr) + del nl[name] + elif t == 'I': + print "\t\tcase %s : setUInt32EEPROM(JettyEEPROM.%s, (long)%sval%s%s); break;" % (name.ljust(26), name, sl, s, sr) + del nl[name] + elif t == 'i': + print "\t\tcase %s : setInt32EEPROM(JettyEEPROM.%s, (int)%sval%s%s); break;" % (name.ljust(26), name, sl, s, sr) + del nl[name] +print "\t\tdefault : Base.logger.log(Level.DEBUG, \"setEEPROMParam(\" + param + \", \" + val + \") call failed\"); break;" +print "\t\t}" +print "\t}" +print "" + +for k in nl: + print "\t// Unhandled set:", k diff --git a/firmware/src/SConscript.motherboard b/firmware/src/SConscript.motherboard index 039b084..0822937 100644 --- a/firmware/src/SConscript.motherboard +++ b/firmware/src/SConscript.motherboard @@ -23,6 +23,7 @@ # # To build for another platform, pass an explicit platform parameter. For example, # $ scons platform=rrmbv12 +# $ scons platform=rrmbv12-2560 # $ scons platform=mb24 # $ scons platform=mb24-2560 # @@ -43,6 +44,9 @@ platform = ARGUMENTS.get('platform','mb24') fived = ARGUMENTS.get('fived','false') f_cpu='16000000L' +# Get the svn version number and use the last part +svn_version = os.popen('svnversion ../..').read()[:-1] +svn_version = svn_version.split(':')[-1] def parse_version(v): if not v: @@ -70,6 +74,14 @@ if (platform == 'rrmbv12'): mcu='atmega644p' has_queue = 1 has_psu = 1 + fived = 'true' +elif (platform == 'rrmbv12-2560'): + default_baud = '115200' + default_programmer = 'stk500v2' + mcu='atmega2560' + has_queue = 1 + has_psu = 0 + fived = 'true' elif (platform == 'mb24'): default_baud = '57600' default_programmer = 'stk500v1' @@ -86,6 +98,7 @@ elif (platform == 'mb24-2560'): else: print "Platform "+platform+" is not currently supported. Supported platforms are:" print "rrmbv12: Gen 3 motherboard" + print "rrmbv12-2560: Gen 3 motherboard for testing on Gen4" print "mb24: Gen4 motherboard" print "mb24-2560: Gen4 motherboard with Mega 2560" exit() @@ -95,20 +108,46 @@ upload_port = ARGUMENTS.get('port','/dev/ttyUSB0') upload_baud = ARGUMENTS.get('baud',default_baud) upload_prog = ARGUMENTS.get('programmer',default_programmer) -srcs = Glob('*.cc') + Glob('Motherboard/*.cc') + Glob('Motherboard/lib_sd/*.c') + Glob('Motherboard/boards/%s/*.cc' % platform) + Glob('shared/*.cc') +srcs = Glob('*.cc') + Glob('Motherboard/*.cc') + Glob('Motherboard/lib_sd/*.c') + Glob('Motherboard/boards/%s/*.cc' % platform) + Glob('shared/*.cc') + Glob('shared/avrfix/*.c') + +include_paths = ['shared', 'Motherboard','shared/avrfix','Motherboard/boards/%s' % platform, '.'] + +# __PROG_TYPES_COMPAT__ +# gcc 4.6.3 and later disallows the PROGEM attribute on data types. For the +# time being, we're using __PROG_TYPES_COMPAT__ for backwards compatibilty -include_paths = ['shared', 'Motherboard','Motherboard/boards/%s' % platform, '.'] +# __DELAY_BACKWARD_COMPATIBLE__ +# The current avrlibc 1.8.0 of July 2012 has a bug whereby it incorrectly +# determines whether __builtin_avr_delay_cycles() is available. The +# configure script makes a small stub program complete with a prototype +# for __builtin_avr_delay_cycles(). It then compiles it *without* +# linking (-S) and then concludes that __builtin_avr_delay_cycles() is +# available. Unfortunately, that test is woefully insufficient and +# proves nothing. But owing to that test, the configure script +# decides that __builtin_avr_delay_cycles() is present and sets +# __HAVE_DELAY_CYCLES to 1 in avr/builtins.h (template=builtins.h.in). +# That in turn makes avr/util/delay.h try to use the non-existent +# __builtin_avr_delay_cycles() routine. To work around that problem, +# it is necessary to set __DELAY_BACKWARD_COMPATIBLE__. flags=[ '-DF_CPU='+str(f_cpu), '-DVERSION='+str(version), + '-DSVN_VERSION='+str(svn_version), + '-D__PROG_TYPES_COMPAT__', + '-D__DELAY_BACKWARD_COMPATIBLE__', '-mmcu='+mcu, '-g', '-Os', '-w', '-fno-exceptions', '-ffunction-sections', - '-fdata-sections'] + '-fdata-sections', + '-DMULKD', + '-DSQRT', + '-DCORDICHK', + '-DROUNDKD', + '-DDIVKD'] if (os.environ.has_key('BUILD_NAME')): flags.append('-DBUILD_NAME=' + os.environ['BUILD_NAME']) @@ -128,6 +167,7 @@ else: if (fived == 'true'): flags.append('-DFOURTH_STEPPER=1') +# flags.append('-mcall-prologues') This saves about 4K of space at the expense of speed, disabled for now env=Environment(tools=['g++', 'gcc'], CC=avr_tools_path+"/avr-g++", @@ -149,7 +189,8 @@ env.Elf(elf_name, objs) env.Hex(hex_name, elf_name) avrdude = avr_tools_path+"/avrdude" -avrdude_flags = "-F -V -p "+mcu.replace("atmega","m") +avrdude_flags = "-p "+mcu.replace("atmega","m") +# avrdude_flags = "-F -V -p "+mcu.replace("atmega","m") - Original flags avrdude_flags = avrdude_flags + " -P "+upload_port avrdude_flags = avrdude_flags + " -c "+upload_prog avrdude_flags = avrdude_flags + " -b "+upload_baud diff --git a/firmware/src/repg_machines/3G-5D-(RPM)-accelerated.xml b/firmware/src/repg_machines/3G-5D-(RPM)-accelerated.xml new file mode 100644 index 0000000..b7e9da0 --- /dev/null +++ b/firmware/src/repg_machines/3G-5D-(RPM)-accelerated.xml @@ -0,0 +1,243 @@ + + + + 3G 5D (RPM) Cupcake (Gen3 XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D (RPM) Cupcake+ABP (Gen3 XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D (RPM) Cupcake (Gen4 XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D (RPM) Cupcake+ABP (Gen4 XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D (RPM) Cupcake (Pololu XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D (RPM) Cupcake+ABP (Pololu XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D (RPM) Cupcake (Pololu XYZ, Mk5/6+Pololu Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D (RPM) Cupcake+ABP (Pololu XYZ, Mk5/6+Pololu Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + diff --git a/firmware/src/repg_machines/3G-5D-accelerated.xml b/firmware/src/repg_machines/3G-5D-accelerated.xml new file mode 100644 index 0000000..ef94487 --- /dev/null +++ b/firmware/src/repg_machines/3G-5D-accelerated.xml @@ -0,0 +1,243 @@ + + + + 3G 5D Cupcake (Gen3 XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D Cupcake+ABP (Gen3 XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D Cupcake (Gen4 XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D Cupcake+ABP (Gen4 XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D Cupcake (Pololu XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D Cupcake+ABP (Pololu XYZ, Mk5/6+Gen4 Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D Cupcake (Pololu XYZ, Mk5/6+Pololu Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + + 3G 5D Cupcake+ABP (Pololu XYZ, Mk5/6+Pololu Extruder, Accelerated) + + + + + + + + + + + + + + + 38400 + 8 + + + + + + + + (Turn off steppers after a build.) + M18 + + + + diff --git a/firmware/src/repg_machines/thingomatic-accelerated.xml b/firmware/src/repg_machines/thingomatic-accelerated.xml new file mode 100644 index 0000000..1b45a1b --- /dev/null +++ b/firmware/src/repg_machines/thingomatic-accelerated.xml @@ -0,0 +1,129 @@ + + + + + + + Thingomatic w/ HBP and Extruder MK6 (Accelerated) + + + + + + + + + + + + + + 115200 + + + + +(Turn off steppers after a build.) +M18 + + + + + Thingomatic w/ ABP and Extruder MK6 (Accelerated) + + + + + + + + + + + + + + 115200 + + + + +(Turn off steppers after a build.) +M18 + + + + + Thingomatic w/ ABP and Stepstruder MK7 (Accelerated) + + + + + + + + + + + + + + 115200 + + + + +(Turn off steppers after a build.) +M18 + + + + + Thingomatic w/ HBP and Stepstruder MK7 (Accelerated) + + + + + + + + + + + + + + 115200 + + + + +(Turn off steppers after a build.) +M18 + + + + diff --git a/firmware/src/shared/Commands.hh b/firmware/src/shared/Commands.hh index 6b93bdd..10b1797 100644 --- a/firmware/src/shared/Commands.hh +++ b/firmware/src/shared/Commands.hh @@ -1,3 +1,4 @@ + /* * Copyright 2010 by Adam Mayer * @@ -104,11 +105,22 @@ #define HOST_CMD_QUEUE_POINT_NEW 142 #define HOST_CMD_STORE_HOME_POSITION 143 #define HOST_CMD_RECALL_HOME_POSITION 144 +#define HOST_CMD_RESET_TO_FACTORY 152 +#define HOST_CMD_SET_MAX_ACCEL 201 +#define HOST_CMD_SET_MAX_FEEDRATE 203 +#define HOST_CMD_SET_DEFAULT_ACCEL 204 +#define HOST_CMD_SET_ADVANCED_ACCEL 205 +#define HOST_CMD_SET_ADVANCED_ACCEL2 206 +#define HOST_CMD_SET_ADVANCE_K 207 +#define HOST_CMD_SET_EXTRUDER_STEPSMM 208 +#define HOST_CMD_SET_ACCELERATION 209 #define HOST_CMD_MOOD_LIGHT_SET_RGB 210 #define HOST_CMD_MOOD_LIGHT_SET_HSB 211 #define HOST_CMD_MOOD_LIGHT_PLAY_SCRIPT 212 #define HOST_CMD_BUZZER_REPEATS 213 #define HOST_CMD_BUZZER_BUZZ 214 +#define HOST_CMD_SET_AXIS_STEPS_MM 215 +#define HOST_CMD_SET_MAX_SPEED_CHANGE 216 #define HOST_CMD_DEBUG_ECHO 0x70 diff --git a/firmware/src/shared/Eeprom.cc b/firmware/src/shared/Eeprom.cc index cbb4a65..b74cc36 100644 --- a/firmware/src/shared/Eeprom.cc +++ b/firmware/src/shared/Eeprom.cc @@ -1,9 +1,15 @@ #include "Eeprom.hh" #include "EepromMap.hh" +#include "EepromDefaults.hh" #include "Version.hh" #include +#ifdef EEPROM_MENU_ENABLE + #include + #include "SDCard.hh" +#endif + namespace eeprom { void init() { @@ -11,7 +17,7 @@ void init() { eeprom_read_block(version,(const uint8_t*)eeprom::VERSION_LOW,2); if ((version[1]*100+version[0]) == firmware_version) return; if (version[1] == 0xff || version[1] < 2) { - setDefaults(); + setDefaults(false); } // Write version version[0] = firmware_version % 100; @@ -19,13 +25,88 @@ void init() { eeprom_write_block(version,(uint8_t*)eeprom::VERSION_LOW,2); } -uint8_t getEeprom8(const uint16_t location, const uint8_t default_value) { +#if defined(ERASE_EEPROM_ON_EVERY_BOOT) || defined(EEPROM_MENU_ENABLE) + +#if defined (__AVR_ATmega168__) + #define EEPROM_SIZE 512 +#elif defined (__AVR_ATmega328__) + #define EEPROM_SIZE 1024 +#elif defined (__AVR_ATmega644P__) + #define EEPROM_SIZE 2048 +#elif defined (__AVR_ATmega1280__) || defined (__AVR_ATmega2560__) + #define EEPROM_SIZE 4096 +#else + #define EEPROM_SIZE 0 +#endif + +//Complete erase of eeprom to 0xFF +void erase() { + for (uint16_t i = 0; i < EEPROM_SIZE; i ++ ) { + eeprom_write_byte((uint8_t*)i, 0xFF); + wdt_reset(); + } +} + +#endif + +#ifdef EEPROM_MENU_ENABLE + +//Saves the eeprom to filename on the sd card +bool saveToSDFile(const char *filename) { + uint8_t v; + + //Open the file for writing + if ( sdcard::startCapture((char *)filename) != sdcard::SD_SUCCESS ) return false; + + //Write the eeprom contents to the file + for (uint16_t i = 0; i < EEPROM_SIZE; i ++ ) { + v = eeprom_read_byte((uint8_t*)i); + sdcard::writeByte(v); + wdt_reset(); + } + + sdcard::finishCapture(); + + return true; +} + +//Restores eeprom from filename on the sdcard +bool restoreFromSDFile(const char *filename) { + uint8_t v; + + if ( sdcard::startPlayback((char *)filename) != sdcard::SD_SUCCESS ) return false; + + for (uint16_t i = 0; i < EEPROM_SIZE; i ++ ) { + if ( sdcard::playbackHasNext() ) { + v = sdcard::playbackNext(); + eeprom_write_byte((uint8_t*)i, v); + wdt_reset(); + } + else break; + } + + sdcard::finishPlayback(); + + return true; +} + +#endif + +uint8_t getEeprom8Sub(const uint16_t location, const uint8_t default_value, const bool defaultCheck) { uint8_t data; eeprom_read_block(&data,(const uint8_t*)location,1); - if (data == 0xff) data = default_value; + if (( defaultCheck ) && (data == 0xff)) data = default_value; return data; } +uint8_t getEeprom8(const uint16_t location, const uint8_t default_value) { + return getEeprom8Sub(location, default_value, true); +} + +uint8_t getEeprom8(const uint16_t location) { + return getEeprom8Sub(location, 0, false); +} + uint16_t getEeprom16(const uint16_t location, const uint16_t default_value) { uint16_t data; eeprom_read_block(&data,(const uint8_t*)location,2); @@ -73,9 +154,13 @@ void putEepromUInt32(const uint16_t location, const uint32_t value) { eeprom_write_block(data,(void*)location,4); } -int64_t getEepromStepsPerMM(const uint16_t location, const int64_t default_value) { +#ifdef STEPS_PER_MM_LOWER_LIMIT + +int64_t getEepromStepsPerMMSub(const uint16_t location, const int64_t default_value, const bool defaultCheck) { int64_t value = eeprom::getEepromInt64(location, default_value); + if ( ! defaultCheck ) return value; + if (( value <= STEPS_PER_MM_LOWER_LIMIT ) || ( value >= STEPS_PER_MM_UPPER_LIMIT )) { eeprom::putEepromInt64(location, default_value); @@ -86,4 +171,14 @@ int64_t getEepromStepsPerMM(const uint16_t location, const int64_t default_value return value; } +int64_t getEepromStepsPerMM(const uint16_t location, const int64_t default_value) { + return getEepromStepsPerMMSub(location, default_value, true); +} + +int64_t getEepromStepsPerMM(const uint16_t location) { + return getEepromStepsPerMMSub(location, 0, false); +} + +#endif + } // namespace eeprom diff --git a/firmware/src/shared/Eeprom.hh b/firmware/src/shared/Eeprom.hh index c3c54cb..70cac85 100644 --- a/firmware/src/shared/Eeprom.hh +++ b/firmware/src/shared/Eeprom.hh @@ -2,11 +2,23 @@ #define EEPROM_HH #include +#include "Configuration.hh" namespace eeprom { void init(); +#if defined(ERASE_EEPROM_ON_EVERY_BOOT) || defined(EEPROM_MENU_ENABLE) + void erase(); +#endif + +#ifdef EEPROM_MENU_ENABLE + bool saveToSDFile(const char *filename); + bool restoreFromSDFile(const char *filename); +#endif + + +uint8_t getEeprom8(const uint16_t location); uint8_t getEeprom8(const uint16_t location, const uint8_t default_value); uint16_t getEeprom16(const uint16_t location, const uint16_t default_value); float getEepromFixed16(const uint16_t location, const float default_value); @@ -14,17 +26,8 @@ int64_t getEepromInt64(const uint16_t location, const int64_t default_value); uint32_t getEepromUInt32(const uint16_t location, const uint32_t default_value); void putEepromInt64(const uint16_t location, const int64_t value); void putEepromUInt32(const uint16_t location, const uint32_t value); +int64_t getEepromStepsPerMM(const uint16_t location); int64_t getEepromStepsPerMM(const uint16_t location, const int64_t default_value); } -#define STEPS_PER_MM_PADDING 5 -#define STEPS_PER_MM_PRECISION 10 -#define STEPS_PER_MM_LOWER_LIMIT 10000000 -#define STEPS_PER_MM_UPPER_LIMIT 200000000000000 -#define STEPS_PER_MM_X_DEFAULT 470698520000 //47.069852 -#define STEPS_PER_MM_Y_DEFAULT 470698520000 //47.069852 -#define STEPS_PER_MM_Z_DEFAULT 2000000000000 //200.0 -#define STEPS_PER_MM_A_DEFAULT 502354788069 //50.2354788069 -#define STEPS_PER_MM_B_DEFAULT 502354788069 //50.2354788069 - #endif // EEPROM_HH diff --git a/firmware/src/shared/InterfaceBoard.cc b/firmware/src/shared/InterfaceBoard.cc index 2a5614f..4d00bc6 100644 --- a/firmware/src/shared/InterfaceBoard.cc +++ b/firmware/src/shared/InterfaceBoard.cc @@ -24,7 +24,7 @@ InterfaceBoard::InterfaceBoard(ButtonArray& buttons_in, void InterfaceBoard::init() { buttons.init(); - lcd.begin(LCD_SCREEN_WIDTH, LCD_SCREEN_HEIGHT); + lcd.begin(lcd.getDisplayWidth(), lcd.getDisplayHeight()); lcd.clear(); lcd.home(); diff --git a/firmware/src/shared/InterfaceBoard.hh b/firmware/src/shared/InterfaceBoard.hh index 86d4adc..2c80623 100644 --- a/firmware/src/shared/InterfaceBoard.hh +++ b/firmware/src/shared/InterfaceBoard.hh @@ -28,13 +28,6 @@ /// Maximum number of screens that can be active at once. #define SCREEN_STACK_DEPTH 7 -/// Character LCD screen geometry -/// -/// Porting Note: Screens may need to be rewritten to support different sizes. -#define LCD_SCREEN_WIDTH 16 -#define LCD_SCREEN_HEIGHT 4 - - /// The InterfaceBoard module provides support for the MakerBot Industries /// Gen4 Interface Board. It could very likely be adopted to support other /// LCD/button setups as well. diff --git a/firmware/src/shared/LiquidCrystal.cc b/firmware/src/shared/LiquidCrystal.cc index c947a7c..48533be 100755 --- a/firmware/src/shared/LiquidCrystal.cc +++ b/firmware/src/shared/LiquidCrystal.cc @@ -1,8 +1,42 @@ #include "LiquidCrystal.hh" +#include "Configuration.hh" + +#ifdef HAS_INTERFACE_BOARD #include #include #include +#include +#include +#include "Eeprom.hh" +#include "EepromMap.hh" +#include "EepromDefaults.hh" + + +//Custom display characters (Courtesy of Tonokip) + +uint8_t degree[8] = +{ + 0x0C, //01100 + 0x12, //10010 + 0x12, //10010 + 0x0C, //01100 + 0x00, //00000 + 0x00, //00000 + 0x00, //00000 + 0x00, //00000 +}; + +uint8_t thermometer[8] = { + 0x04, //00100 + 0x0A, //01010 + 0x0A, //01010 + 0x0A, //01010 + 0x0A, //01010 + 0x11, //10001 + 0x11, //10001 + 0x0E, //01110 +}; // When the display powers up, it is configured as follows: // @@ -49,6 +83,33 @@ LiquidCrystal::LiquidCrystal(Pin rs, Pin enable, init(1, rs, Pin(), enable, d0, d1, d2, d3, Pin(), Pin(), Pin(), Pin()); } +void LiquidCrystal::reloadDisplayType(void) { + uint8_t lcdType = eeprom::getEeprom8(eeprom::LCD_TYPE, EEPROM_DEFAULT_LCD_TYPE); + switch ( lcdType ) { + case LCD_TYPE_16x4: + _displayWidth = 16; + _clearDelay = 2000; + break; + case LCD_TYPE_20x4: + _displayWidth = 20; + _clearDelay = 15000; + break; + case LCD_TYPE_24x4: + _displayWidth = 24; + _clearDelay = 15000; + break; + default: + //If we're unrecognized, default to 16x4 + eeprom_write_byte((uint8_t*)eeprom::LCD_TYPE, EEPROM_DEFAULT_LCD_TYPE); + _displayWidth = 16; + _clearDelay = 2000; + break; + + } + + begin(_displayWidth, 1); +} + void LiquidCrystal::init(uint8_t fourbitmode, Pin rs, Pin rw, Pin enable, Pin d0, Pin d1, Pin d2, Pin d3, Pin d4, Pin d5, Pin d6, Pin d7) @@ -75,8 +136,29 @@ void LiquidCrystal::init(uint8_t fourbitmode, Pin rs, Pin rw, Pin enable, _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; else _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS; - - begin(16, 1); + + reloadDisplayType(); +} + +void LiquidCrystal::nextLcdType(void) { + uint8_t newLcdType; + uint8_t lcdType = eeprom::getEeprom8(eeprom::LCD_TYPE, EEPROM_DEFAULT_LCD_TYPE); + + switch ( lcdType ) { + case LCD_TYPE_16x4: + newLcdType = LCD_TYPE_20x4; + break; + case LCD_TYPE_20x4: + newLcdType = LCD_TYPE_24x4; + break; + case LCD_TYPE_24x4: + newLcdType = LCD_TYPE_16x4; + break; + } + + cli(); + eeprom_write_byte((uint8_t*)eeprom::LCD_TYPE,newLcdType); + sei(); } void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { @@ -152,13 +234,16 @@ void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { // set the entry mode command(LCD_ENTRYMODESET | _displaymode); + //Create 2 custom characters + createChar(1,degree); + createChar(2,thermometer); } /********** high level commands, for the user! */ void LiquidCrystal::clear() { command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero - _delay_us(2000); // this command takes a long time! + _delay_us(_clearDelay); // this command takes a long time! } void LiquidCrystal::home() @@ -169,7 +254,7 @@ void LiquidCrystal::home() void LiquidCrystal::setCursor(uint8_t col, uint8_t row) { - int row_offsets[] = { 0x00, 0x40, 0x10, 0x50 }; + int row_offsets[] = { 0x00, 0x40, _displayWidth, 0x40 + _displayWidth }; if ( row > _numlines ) { row = _numlines-1; // we count rows starting w/0 } @@ -246,6 +331,7 @@ void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) { command(LCD_SETCGRAMADDR | (location << 3)); for (int i=0; i<8; i++) { write(charmap[i]); + _delay_us(4500); // wait min 4.5ms } } @@ -430,3 +516,13 @@ void LiquidCrystal::write8bits(uint8_t value) { pulseEnable(); } + +uint8_t LiquidCrystal::getDisplayWidth() { + return _displayWidth; +} + +uint8_t LiquidCrystal::getDisplayHeight() { + return 4; +} + +#endif diff --git a/firmware/src/shared/LiquidCrystal.hh b/firmware/src/shared/LiquidCrystal.hh index e729727..70d8002 100755 --- a/firmware/src/shared/LiquidCrystal.hh +++ b/firmware/src/shared/LiquidCrystal.hh @@ -61,7 +61,9 @@ public: void init(uint8_t fourbitmode, Pin rs, Pin rw, Pin enable, Pin d0, Pin d1, Pin d2, Pin d3, Pin d4, Pin d5, Pin d6, Pin d7); - + + void reloadDisplayType(void); + void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); void clear(); @@ -97,6 +99,10 @@ public: void command(uint8_t); + uint8_t getDisplayWidth(); + uint8_t getDisplayHeight(); + void nextLcdType(); + private: void send(uint8_t, bool); void write4bits(uint8_t); @@ -115,6 +121,9 @@ private: uint8_t _initialized; uint8_t _numlines,_currline; + + uint8_t _displayWidth; + uint16_t _clearDelay; }; #endif // LIQUID_CRYSTAL_HH diff --git a/firmware/src/shared/Marlin-issues.txt b/firmware/src/shared/Marlin-issues.txt new file mode 100644 index 0000000..bdb6192 --- /dev/null +++ b/firmware/src/shared/Marlin-issues.txt @@ -0,0 +1,114 @@ +1. [Status: Not an issue for Jetty firmware] + + About the commented out cosine based jerk calcs in planner.cpp. Originally, + that code could NOT have worked correctly because the distance used + to normalize the vectors in the dot product used the extruder distance. + Whereas, the dot product in the cosine code only used the x, y, and z axes. + Thus, the normalization of the (x,y,z) vector was incorrect. Either that, + or the extruder axis component should have been included in the vector dot + product. Owing to that error, an incorrect value was computed for the + cosine of the angle for every move which included extruder steps! + + Now that Marlin has changed the distance calculation to no longer + include the extruder axis in the distance, that cosine jerk code + should be retested. + + See commit 6ef8459 to ErikZalm/Marlin for the change to the distance calc, + + https://github.com/ErikZalm/Marlin/commit/6ef8459494cd5ffdc1b2098f81a8346e006c8657 + +2. [Status: fixed in Jetty firmware in r248 on 28 March 2012. Was causing + z-axis slippage and other unexpected speed increases.] + + It's possible for block->final_rate to be set larger than + block->nominal_rate. This occurs when the next block's entry_speed + is larger than the current block's nominal speed. In that case, + planner_recalculate_trapezoids() makes the call + + calculate_trapezoid_for_block(current, + current->entry_speed/current->nominal_speed, + next->entry_speed/current->nominal_speed); + + Since next->entry_speed > current->nominal_speed, an exit_factor > 1 is + passed to calculate_trapezoid_for_block(). This, in turn, causes + calculate_trapezoid_for_block() to set the final_rate to a value + larger than the nominal_rate. That then causes any of the max feed + rate limits for the block to then be superceded. For example, if the + block was primarily an extruder move with a critical max feed rate, then + that feed rate may be exceeded by putting the final_rate > nominal_rate. + + While further passes through the planner might rectify this, it's always + possible that st_interrupt() will pick up and process the block before + this error is mollified. + + One fix is to have calculate_trapezoid_for_block() always check + entry_ and exit_factor and ensure that they do not exceed 1. + +3. [Status: indirectly fixed in Jetty Firmware by virtue of fixing Issue + 2 above.] + + In st_interrupt() there can be a sudden speed jump in cases where + block->final_rate > block->nominal_rate. [See 2. above for cases + where this happens.] This sudden speed jump occurs in the deceleration + phase when the following check is performed, + + if(step_rate > acc_step_rate) { // Check step_rate stays positive + step_rate = current_block->final_rate; + } + else { + step_rate = acc_step_rate - step_rate; // Decelerate from aceleration end point. + } + + // lower limit + if(step_rate < current_block->final_rate) + step_rate = current_block->final_rate; + + Since acc_step_rate <= nominal_rate and nominal_rate < final_rate, + either of those limit checks will simply set step_rate to be final_rate. + As a result, as soon as the deceleration phase is entered, the stepper + rate immediately jumps to final_rate. + +4. [Status: fixed in Jetty firmware in r91 on 9 March 2012.] + + In stepper.cpp, calling calc_timer() has a known side-effect of changing the + global variable step_loops. Consequently, when trapezoid_generator_reset() + sets up for the next block, it should call calc_timer() first for the + nominal (plateau) rate and then call calc_timer() for the initial_rate + (acceleration phase). Because it calls calc_timer() for the nominal + rate AFTER calling it for initial_rate, step_loops has the wrong value + when st_interrupt() begins handling the first set of acceleration steps. + + trapezoid_generator_reset() should be changed from + + acc_step_rate = current_block->initial_rate; + acceleration_time = calc_timer(acc_step_rate); + OCR1A = acceleration_time; + OCR1A_nominal = calc_timer(current_block->nominal_rate); + + to + + OCR1A_nominal = calc_timer(current_block->nominal_rate); + acc_step_rate = current_block->initial_rate; + acceleration_time = calc_timer(acc_step_rate); + OCR1A = acceleration_time; + +5. [Status: fixed in Jetty firmware in r43 on 1 March 2012.] + + In planner_reverse_pass(), the while loop can run through blocks it + shouldn't. At issue is the fact that block_buffer_tail can change + everytime st_interrupt() runs and releases a block. In the following + example, the while loop should stop after index == 4. However, it + won't as the tail gets incremented to 5 before the test which whould + have terminated the loop occurs. + +main line code: index = 5, tail = 4; index != tail +main line code: index = prev_index() --> index = 4 +interrupt: increments tail to 5 +main line code: index = 4; tail = 5; index != tail +… main line code now chugs along, doing index = 3, index = 2, … +… main line code goes through blocks it shouldn't have + + planner_reverse_pass() needs to save a local copy of block_buffer_tail() + before the loop begins. + +-- finit -- diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index 6df37ad..a06421f 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -1,9 +1,18 @@ +// Future things that could be consolidated into 1 to save code space when required: +// +// ValueSetScreen +// BuzzerSetRepeatsMode +// ABPCopiesSetScreen +// EStepsPerMMLengthMode +// EStepsPerMMStepsMode + #include "Menu.hh" #include "Configuration.hh" // TODO: Kill this, should be hanlded by build system. #ifdef HAS_INTERFACE_BOARD +#include "StepperAccel.hh" #include "Steppers.hh" #include "Commands.hh" #include "Errors.hh" @@ -19,6 +28,7 @@ #include "SDCard.hh" #include "EepromMap.hh" #include "Eeprom.hh" +#include "EepromDefaults.hh" #include #include "ExtruderControl.hh" @@ -31,6 +41,8 @@ #define MAX_ITEMS_PER_SCREEN 4 +#define LCD_TYPE_CHANGE_BUTTON_HOLD_TIME 10.0 + int16_t overrideExtrudeSeconds = 0; bool estimatingBuild = false; @@ -49,6 +61,9 @@ enum Axis { AXIS_B }; +//Macros to expand SVN revision macro into a str +#define STR_EXPAND(x) #x //Surround the supplied macro by double quotes +#define STR(x) STR_EXPAND(x) //Stack checking //http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=52249 @@ -291,11 +306,10 @@ int appendUint8(char *buf, uint8_t buflen, uint8_t val) void SplashScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar splash1[] = " Thing-O-Matic "; - const static PROGMEM prog_uchar splash2[] = " --------- "; - const static PROGMEM prog_uchar splash3[] = " Jetty Firmware "; - const static PROGMEM prog_uchar splash4[] = "Thing 15380 3.0b"; - + const static PROGMEM prog_uchar splash1[] = " Jetty Firmware "; + const static PROGMEM prog_uchar splash2[] = " ------------- "; + const static PROGMEM prog_uchar splash3[] = "Thing 15380 3.4z"; + const static PROGMEM prog_uchar splash4[] = " Revision: ____ "; if (forceRedraw) { lcd.setCursor(0,0); @@ -309,6 +323,8 @@ void SplashScreen::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,3); lcd.writeFromPgmspace(splash4); + lcd.setCursor(11,3); + lcd.writeString(STR(SVN_VERSION)); } else { // The machine has started, so we're done! @@ -329,7 +345,7 @@ UserViewMenu::UserViewMenu() { } void UserViewMenu::resetState() { - uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0); + uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, EEPROM_DEFAULT_JOG_MODE_SETTINGS); if ( jogModeSettings & 0x01 ) itemIndex = 3; else itemIndex = 2; @@ -358,7 +374,7 @@ void UserViewMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { } void UserViewMenu::handleSelect(uint8_t index) { - uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0); + uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, EEPROM_DEFAULT_JOG_MODE_SETTINGS); switch (index) { case 2: @@ -374,8 +390,96 @@ void UserViewMenu::handleSelect(uint8_t index) { } } +void JoggerMenu::jog(ButtonArray::ButtonName direction, bool pauseModeJog) { + Point position = steppers::getPosition(); + int32_t interval = 1000; + + float speed = 1.5; //In mm's + + if ( pauseModeJog ) jogDistance = DISTANCE_CONT; + else { + switch(jogDistance) { + case DISTANCE_0_1MM: + speed = 0.1; //0.1mm + break; + case DISTANCE_1MM: + speed = 1.0; //1mm + break; + case DISTANCE_CONT: + speed = 1.5; //1.5mm + break; + } + } + + //Reverse direction of X and Y if we're in User View Mode and + //not model mode + int32_t vMode = 1; + if ( userViewMode ) vMode = -1; + + float stepsPerSecond; + enum Axis axisIndex = AXIS_X; + uint16_t eepromLocation = eeprom::HOMING_FEED_RATE_X; + + switch(direction) { + case ButtonArray::XMINUS: + position[0] -= vMode * mmToSteps(speed,AXIS_X); + eepromLocation = eeprom::HOMING_FEED_RATE_X; axisIndex = AXIS_X; + break; + case ButtonArray::XPLUS: + position[0] += vMode * mmToSteps(speed,AXIS_X); + eepromLocation = eeprom::HOMING_FEED_RATE_X; axisIndex = AXIS_X; + break; + case ButtonArray::YMINUS: + position[1] -= vMode * mmToSteps(speed,AXIS_Y); + eepromLocation = eeprom::HOMING_FEED_RATE_Y; axisIndex = AXIS_Y; + break; + case ButtonArray::YPLUS: + position[1] += vMode * mmToSteps(speed,AXIS_Y); + eepromLocation = eeprom::HOMING_FEED_RATE_Y; axisIndex = AXIS_Y; + break; + case ButtonArray::ZMINUS: + position[2] -= mmToSteps(speed,AXIS_Z); + eepromLocation = eeprom::HOMING_FEED_RATE_Z; axisIndex = AXIS_Z; + break; + case ButtonArray::ZPLUS: + position[2] += mmToSteps(speed,AXIS_Z); + eepromLocation = eeprom::HOMING_FEED_RATE_Z; axisIndex = AXIS_Z; + break; + case ButtonArray::OK: + case ButtonArray::ZERO: + if ( ! pauseModeJog ) break; + + float mms = (float)eeprom::getEeprom8(eeprom::EXTRUDE_MMS, EEPROM_DEFAULT_EXTRUDE_MMS); + float eStepsPerMM = (float)eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM) / 10.0; + stepsPerSecond = mms * eStepsPerMM; + interval = (int32_t)(1000000.0 / stepsPerSecond); + + //Handle reverse + if ( direction == ButtonArray::OK ) stepsPerSecond *= -1; + + //Handle 5D + if ( eeprom::getEeprom8(eeprom::INVERTED_EXTRUDER_5D, EEPROM_DEFAULT_INVERTED_EXTRUDER_5D) == 1 ) stepsPerSecond *= -1; + + //Extrude for 0.5 seconds + position[3] += (int32_t)(0.5 * stepsPerSecond); + break; + } + + if ( jogDistance == DISTANCE_CONT ) lastDirectionButtonPressed = direction; + else lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + + if ( eepromLocation != 0 ) { + //60.0, because feed rate is in mm/min units, we convert to seconds + float feedRate = (float)eeprom::getEepromUInt32(eepromLocation, 500) / 60.0; + stepsPerSecond = feedRate * (float)mmToSteps(1.0, axisIndex); + interval = (int32_t)(1000000.0 / stepsPerSecond); + } + + steppers::setTarget(position, interval); +} + void JogMode::reset() { - uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0); + uint8_t jogModeSettings = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, EEPROM_DEFAULT_JOG_MODE_SETTINGS); jogDistance = (enum distance_t)((jogModeSettings >> 1 ) & 0x07); if ( jogDistance > DISTANCE_CONT ) jogDistance = DISTANCE_0_1MM; @@ -386,7 +490,7 @@ void JogMode::reset() { userViewMode = jogModeSettings & 0x01; userViewModeChanged = false; - steppers::switchToRegularDriver(); + steppers::switchToRegularDriver(false); } void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { @@ -402,7 +506,7 @@ void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar distance1mm[] = "1mm"; const static PROGMEM prog_uchar distanceCont[] = "Cont.."; - if ( userViewModeChanged ) userViewMode = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, 0) & 0x01; + if ( userViewModeChanged ) userViewMode = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, EEPROM_DEFAULT_JOG_MODE_SETTINGS) & 0x01; if (forceRedraw || distanceChanged || userViewModeChanged) { lcd.clear(); @@ -451,59 +555,6 @@ void JogMode::update(LiquidCrystal& lcd, bool forceRedraw) { } } -void JogMode::jog(ButtonArray::ButtonName direction) { - Point position = steppers::getPosition(); - - int32_t interval = 2000; - float speed; //In mm's - - if ( jogDistance == DISTANCE_CONT ) interval = 1000; - - switch(jogDistance) { - case DISTANCE_0_1MM: - speed = 0.1; //0.1mm - break; - case DISTANCE_1MM: - speed = 1.0; //1mm - break; - case DISTANCE_CONT: - speed = 1.5; //1.5mm - break; - } - - - //Reverse direction of X and Y if we're in User View Mode and - //not model mode - int32_t vMode = 1; - if ( userViewMode ) vMode = -1; - - switch(direction) { - case ButtonArray::XMINUS: - position[0] -= vMode * mmToSteps(speed,AXIS_X); - break; - case ButtonArray::XPLUS: - position[0] += vMode * mmToSteps(speed,AXIS_X); - break; - case ButtonArray::YMINUS: - position[1] -= vMode * mmToSteps(speed,AXIS_Y); - break; - case ButtonArray::YPLUS: - position[1] += vMode * mmToSteps(speed,AXIS_Y); - break; - case ButtonArray::ZMINUS: - position[2] -= mmToSteps(speed,AXIS_Z); - break; - case ButtonArray::ZPLUS: - position[2] += mmToSteps(speed,AXIS_Z); - break; - } - - if ( jogDistance == DISTANCE_CONT ) lastDirectionButtonPressed = direction; - else lastDirectionButtonPressed = (ButtonArray::ButtonName)0; - - steppers::setTarget(position, interval); -} - void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { switch (button) { case ButtonArray::ZERO: @@ -532,7 +583,7 @@ void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { case ButtonArray::ZPLUS: case ButtonArray::XMINUS: case ButtonArray::XPLUS: - jog(button); + jog(button, false); break; case ButtonArray::CANCEL: steppers::abort(); @@ -546,23 +597,23 @@ void JogMode::notifyButtonPressed(ButtonArray::ButtonName button) { } void ExtruderMode::reset() { - extrudeSeconds = (enum extrudeSeconds)eeprom::getEeprom8(eeprom::EXTRUDE_DURATION, 1); + extrudeSeconds = (enum extrudeSeconds)eeprom::getEeprom8(eeprom::EXTRUDE_DURATION, EEPROM_DEFAULT_EXTRUDE_DURATION); updatePhase = 0; timeChanged = false; lastDirection = 1; overrideExtrudeSeconds = 0; - steppers::switchToRegularDriver(); + steppers::switchToRegularDriver(true); } void ExtruderMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar extrude1[] = "Extrude: "; - const static PROGMEM prog_uchar extrude2[] = "(set rpm) Fwd"; + const static PROGMEM prog_uchar extrude2[] = "(set mm/s) Fwd"; const static PROGMEM prog_uchar extrude3[] = " (stop) (dur)"; const static PROGMEM prog_uchar extrude4[] = "---/---C Rev"; const static PROGMEM prog_uchar secs[] = "SECS"; const static PROGMEM prog_uchar blank[] = " "; - if (overrideExtrudeSeconds) extrude(overrideExtrudeSeconds, true); + if (overrideExtrudeSeconds) extrude((int32_t)overrideExtrudeSeconds, true); if (forceRedraw) { lcd.clear(); @@ -620,7 +671,7 @@ void ExtruderMode::update(LiquidCrystal& lcd, bool forceRedraw) { } } -void ExtruderMode::extrude(seconds_t seconds, bool overrideTempCheck) { +void ExtruderMode::extrude(int32_t seconds, bool overrideTempCheck) { //Check we're hot enough if ( ! overrideTempCheck ) { @@ -639,19 +690,18 @@ void ExtruderMode::extrude(seconds_t seconds, bool overrideTempCheck) { Point position = steppers::getPosition(); - float rpm = (float)eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19) / 10.0; - - //60 * 1000000 = # uS in a minute - //200 * 8 = 200 steps per revolution * 1/8 stepping - int32_t interval = (int32_t)(60L * 1000000L) / (int32_t)((float)(200 * 8) * rpm); - int16_t stepsPerSecond = (int16_t)((200.0 * 8.0 * rpm) / 60.0); + float mms = (float)eeprom::getEeprom8(eeprom::EXTRUDE_MMS, EEPROM_DEFAULT_EXTRUDE_MMS); + float eStepsPerMM = (float)eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM) / 10.0; + float stepsPerSecond = mms * eStepsPerMM; + int32_t interval = (int32_t)(1000000.0 / stepsPerSecond); - //50.235479 is ToM stepper extruder speed, we use this as a baseline - stepsPerSecond = (int16_t)((float)stepsPerSecond * stepsToMM((int32_t)50.235479, AXIS_A)); + //Handle 5D + float direction5d = 1.0; + if ( eeprom::getEeprom8(eeprom::INVERTED_EXTRUDER_5D, EEPROM_DEFAULT_INVERTED_EXTRUDER_5D) == 1 ) direction5d = -1.0; if ( seconds == 0 ) steppers::abort(); else { - position[3] += seconds * stepsPerSecond; + position[3] += direction5d * seconds * stepsPerSecond; steppers::setTarget(position, interval); } @@ -700,32 +750,37 @@ void ExtruderMode::notifyButtonPressed(ButtonArray::ButtonName button) { //If we're already extruding, change the time running if (steppers::isRunning()) - extrude((seconds_t)(zReverse * lastDirection * extrudeSeconds), false); + extrude((int32_t)(zReverse * lastDirection * extrudeSeconds), false); timeChanged = true; break; case ButtonArray::YPLUS: - // Show Extruder RPM Setting Screen - interface::pushScreen(&extruderSetRpmScreen); + // Show Extruder MMS Setting Screen + extruderSetMMSScreen.location = eeprom::EXTRUDE_MMS; + extruderSetMMSScreen.default_value = EEPROM_DEFAULT_EXTRUDE_MMS; + extruderSetMMSScreen.message1 = "Extruder speed:"; + extruderSetMMSScreen.units = " mm/s "; + interface::pushScreen(&extruderSetMMSScreen); break; case ButtonArray::ZERO: case ButtonArray::YMINUS: case ButtonArray::XMINUS: case ButtonArray::XPLUS: - extrude((seconds_t)EXTRUDE_SECS_CANCEL, true); + extrude((int32_t)EXTRUDE_SECS_CANCEL, true); break; case ButtonArray::ZMINUS: case ButtonArray::ZPLUS: if ( button == ButtonArray::ZPLUS ) lastDirection = 1; else lastDirection = -1; - extrude((seconds_t)(zReverse * lastDirection * extrudeSeconds), false); + extrude((int32_t)(zReverse * lastDirection * extrudeSeconds), false); break; case ButtonArray::CANCEL: steppers::abort(); steppers::enableAxis(3, false); interface::popScreen(); steppers::switchToAcceleratedDriver(); + steppers::enableAxis(3, false); break; } } @@ -762,6 +817,11 @@ void ExtruderTooColdMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { } } +void ExtruderTooColdMenu::handleCancel() { + overrideExtrudeSeconds = 0; + interface::popScreen(); +} + void ExtruderTooColdMenu::handleSelect(uint8_t index) { switch (index) { case 2: @@ -776,65 +836,6 @@ void ExtruderTooColdMenu::handleSelect(uint8_t index) { } } -void ExtruderSetRpmScreen::reset() { - rpm = eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19); -} - -void ExtruderSetRpmScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar message1[] = "Extruder RPM:"; - const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; - const static PROGMEM prog_uchar blank[] = " "; - - if (forceRedraw) { - lcd.clear(); - - lcd.setCursor(0,0); - lcd.writeFromPgmspace(message1); - - lcd.setCursor(0,3); - lcd.writeFromPgmspace(message4); - } - - // Redraw tool info - lcd.setCursor(0,1); - lcd.writeFloat((float)rpm / 10.0, 1); - lcd.writeFromPgmspace(blank); -} - -void ExtruderSetRpmScreen::notifyButtonPressed(ButtonArray::ButtonName button) { - switch (button) { - case ButtonArray::CANCEL: - interface::popScreen(); - break; - case ButtonArray::ZERO: - break; - case ButtonArray::OK: - eeprom_write_byte((uint8_t *)eeprom::EXTRUDE_RPM, rpm); - interface::popScreen(); - break; - case ButtonArray::ZPLUS: - // increment more - if (rpm <= 250) rpm += 5; - break; - case ButtonArray::ZMINUS: - // decrement more - if (rpm >= 8) rpm -= 5; - break; - case ButtonArray::YPLUS: - // increment less - if (rpm <= 254) rpm += 1; - break; - case ButtonArray::YMINUS: - // decrement less - if (rpm >= 4) rpm -= 1; - break; - case ButtonArray::XMINUS: - case ButtonArray::XPLUS: - break; - } -} - - void MoodLightMode::reset() { updatePhase = 0; scriptId = eeprom_read_byte((uint8_t *)eeprom::MOOD_LIGHT_SCRIPT); @@ -966,9 +967,9 @@ void MoodLightSetRGBScreen::reset() { inputMode = 0; //Red redrawScreen = false; - red = eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_RED, 255);; - green = eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_GREEN, 255);; - blue = eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_BLUE, 255);; + red = eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_RED, EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_RED);; + green = eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_GREEN, EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_GREEN);; + blue = eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_BLUE, EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_BLUE);; } void MoodLightSetRGBScreen::update(LiquidCrystal& lcd, bool forceRedraw) { @@ -1049,128 +1050,6 @@ void MoodLightSetRGBScreen::notifyButtonPressed(ButtonArray::ButtonName button) } } - -void SnakeMode::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar gameOver[] = "GAME OVER!"; - - // If we are dead, restart the game. - if (!snakeAlive) { - reset(); - forceRedraw = true; - } - - if (forceRedraw) { - lcd.clear(); - - for (uint8_t i = 0; i < snakeLength; i++) { - lcd.setCursor(snakeBody[i].x, snakeBody[i].y); - lcd.write('O'); - } - } - - // Always redraw the apple, just in case. - lcd.setCursor(applePosition.x, applePosition.y); - lcd.write('*'); - - // First, undraw the snake's tail - lcd.setCursor(snakeBody[snakeLength-1].x, snakeBody[snakeLength-1].y); - lcd.write(' '); - - // Then, shift the snakes body parts back, deleting the tail - for(int8_t i = snakeLength-1; i >= 0; i--) { - snakeBody[i+1] = snakeBody[i]; - } - - // Create a new head for the snake (this causes it to move forward) - switch(snakeDirection) - { - case DIR_EAST: - snakeBody[0].x = (snakeBody[0].x + 1) % LCD_SCREEN_WIDTH; - break; - case DIR_WEST: - snakeBody[0].x = (snakeBody[0].x + LCD_SCREEN_WIDTH - 1) % LCD_SCREEN_WIDTH; - break; - case DIR_NORTH: - snakeBody[0].y = (snakeBody[0].y + LCD_SCREEN_HEIGHT - 1) % LCD_SCREEN_HEIGHT; - break; - case DIR_SOUTH: - snakeBody[0].y = (snakeBody[0].y + 1) % LCD_SCREEN_HEIGHT; - break; - } - - // Now, draw the snakes new head - lcd.setCursor(snakeBody[0].x, snakeBody[0].y); - lcd.write('O'); - - // Check if the snake has run into itself - for (uint8_t i = 1; i < snakeLength; i++) { - if (snakeBody[i].x == snakeBody[0].x - && snakeBody[i].y == snakeBody[0].y) { - snakeAlive = false; - - lcd.setCursor(1,1); - lcd.writeFromPgmspace(gameOver); - updateRate = 5000L * 1000L; - } - } - - // If the snake just ate an apple, increment count and make new apple - if (snakeBody[0].x == applePosition.x - && snakeBody[0].y == applePosition.y) { - applesEaten++; - - if(applesEaten % APPLES_BEFORE_GROW == 0) { - snakeLength++; - updateRate -= 5L * 1000L; - } - - applePosition.x = rand()%LCD_SCREEN_WIDTH; - applePosition.y = rand()%LCD_SCREEN_HEIGHT; - - lcd.setCursor(applePosition.x, applePosition.y); - lcd.write('*'); - } -} - -void SnakeMode::reset() { - updateRate = 150L * 1000L; - snakeDirection = DIR_EAST; - snakeLength = 3; - applesEaten = 0; - snakeAlive = true; - - // Put the snake in an initial position - snakeBody[0].x = 2; snakeBody[0].y = 1; - snakeBody[1].x = 1; snakeBody[1].y = 1; - snakeBody[2].x = 0; snakeBody[2].y = 1; - - // Put the apple in an initial position (this could collide with the snake!) - applePosition.x = rand()%LCD_SCREEN_WIDTH; - applePosition.y = rand()%LCD_SCREEN_HEIGHT; -} - - -void SnakeMode::notifyButtonPressed(ButtonArray::ButtonName button) { - switch (button) { - case ButtonArray::YMINUS: - snakeDirection = DIR_SOUTH; - break; - case ButtonArray::YPLUS: - snakeDirection = DIR_NORTH; - break; - case ButtonArray::XMINUS: - snakeDirection = DIR_WEST; - break; - case ButtonArray::XPLUS: - snakeDirection = DIR_EAST; - break; - case ButtonArray::CANCEL: - interface::popScreen(); - break; - } -} - - void MonitorMode::reset() { updatePhase = UPDATE_PHASE_FIRST; buildTimePhase = BUILD_TIME_PHASE_FIRST; @@ -1178,16 +1057,19 @@ void MonitorMode::reset() { lastElapsedSeconds = 0.0; pausePushLockout = false; pauseMode.autoPause = false; + pauseMode.noheatPause = false; buildCompleteBuzzPlayed = false; overrideForceRedraw = false; copiesPrinted = 0; timeLeftDisplayed = false; + flashingTool = false; + flashingPlatform = false; } void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar extruder_temp[] = "Tool: ---/---C"; - const static PROGMEM prog_uchar platform_temp[] = "Bed: ---/---C"; + const static PROGMEM prog_uchar extruder_temp[] = "Tool ---/---\001"; + const static PROGMEM prog_uchar platform_temp[] = "Bed ---/---\001"; const static PROGMEM prog_uchar elapsed_time[] = "Elapsed: 0h00m"; const static PROGMEM prog_uchar completed_percent[] = "Completed: 0% "; const static PROGMEM prog_uchar time_left[] = "TimeLeft: 0h00m"; @@ -1195,6 +1077,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar time_left_secs[] = "secs"; const static PROGMEM prog_uchar time_left_none[] = " none"; const static PROGMEM prog_uchar zpos[] = "ZPos: "; + const static PROGMEM prog_uchar speed[] = "Acc: "; const static PROGMEM prog_uchar zpos_mm[] = "mm"; const static PROGMEM prog_uchar estimate2[] = "Estimating: 0%"; const static PROGMEM prog_uchar estimate3[] = " (skip)"; @@ -1207,6 +1090,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( ! pausePushLockout ) { pausePushLockout = true; pauseMode.autoPause = true; + pauseMode.noheatPause = false; interface::pushScreen(&pauseMode); return; } @@ -1218,7 +1102,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { //Check for a build complete, and if we have more than one copy //to print, setup another one if (( ! estimatingBuild ) && ( host::isBuildComplete() )) { - uint8_t copiesToPrint = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); + uint8_t copiesToPrint = eeprom::getEeprom8(eeprom::ABP_COPIES, EEPROM_DEFAULT_ABP_COPIES); if ( copiesToPrint > 1 ) { if ( copiesPrinted < (copiesToPrint - 1)) { copiesPrinted ++; @@ -1274,10 +1158,10 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( estimatingBuild ) { //If preheat_during_estimate is set, then preheat OutPacket responsePacket; - if ( eeprom::getEeprom8(eeprom::PREHEAT_DURING_ESTIMATE, 0) ) { - uint8_t value = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220); + if ( eeprom::getEeprom8(eeprom::PREHEAT_DURING_ESTIMATE, EEPROM_DEFAULT_PREHEAT_DURING_ESTIMATE) ) { + uint8_t value = eeprom::getEeprom8(eeprom::TOOL0_TEMP, EEPROM_DEFAULT_TOOL0_TEMP); extruderControl(SLAVE_CMD_SET_TEMP, EXTDR_CMD_SET, responsePacket, (uint16_t)value); - value = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110); + value = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, EEPROM_DEFAULT_PLATFORM_TEMP); extruderControl(SLAVE_CMD_SET_PLATFORM_TEMP, EXTDR_CMD_SET, responsePacket, value); } @@ -1307,12 +1191,26 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { return; } + + + //Flash the temperature indicators + toggleHeating ^= true; + + if ( flashingTool ) { + lcd.setCursor(5,2); + lcd.write(toggleHeating?' ':2); + } + if ( flashingPlatform ) { + lcd.setCursor(5,3); + lcd.write(toggleHeating?' ':2); + } + OutPacket responsePacket; // Redraw tool info switch (updatePhase) { case UPDATE_PHASE_TOOL_TEMP: - lcd.setCursor(6,2); + lcd.setCursor(7,2); if (extruderControl(SLAVE_CMD_GET_TEMP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); lcd.writeInt(data,3); @@ -1322,17 +1220,29 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { break; case UPDATE_PHASE_TOOL_TEMP_SET_POINT: - lcd.setCursor(10,2); + lcd.setCursor(11,2); + uint16_t data; if (extruderControl(SLAVE_CMD_GET_SP, EXTDR_CMD_GET, responsePacket, 0)) { - uint16_t data = responsePacket.read16(1); + data = responsePacket.read16(1); lcd.writeInt(data,3); } else { lcd.writeString("XXX"); } + + lcd.setCursor(5,2); + if (extruderControl(SLAVE_CMD_IS_TOOL_READY, EXTDR_CMD_GET, responsePacket, 0)) { + flashingTool = false; + uint8_t ready = responsePacket.read8(1); + if ( data != 0 ) { + if ( ready ) lcd.write(2); + else flashingTool = true; + } + else lcd.write(' '); + } break; case UPDATE_PHASE_PLATFORM_TEMP: - lcd.setCursor(6,3); + lcd.setCursor(7,3); if (extruderControl(SLAVE_CMD_GET_PLATFORM_TEMP, EXTDR_CMD_GET, responsePacket, 0)) { uint16_t data = responsePacket.read16(1); lcd.writeInt(data,3); @@ -1342,14 +1252,25 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { break; case UPDATE_PHASE_PLATFORM_SET_POINT: - lcd.setCursor(10,3); + lcd.setCursor(11,3); if (extruderControl(SLAVE_CMD_GET_PLATFORM_SP, EXTDR_CMD_GET, responsePacket, 0)) { - uint16_t data = responsePacket.read16(1); + data = responsePacket.read16(1); lcd.writeInt(data,3); } else { lcd.writeString("XXX"); } + lcd.setCursor(5,3); + if (extruderControl(SLAVE_CMD_IS_PLATFORM_READY, EXTDR_CMD_GET, responsePacket, 0)) { + flashingPlatform = false; + uint8_t ready = responsePacket.read8(1); + if ( data != 0 ) { + if ( ready ) lcd.write(2); + else flashingPlatform = true; + } + else lcd.write(' '); + } + lcd.setCursor(15,3); if ( command::getPauseAtZPos() == 0 ) lcd.write(' '); else lcd.write('*'); @@ -1362,7 +1283,7 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { //Signal buzzer if we're complete if (( ! buildCompleteBuzzPlayed ) && ( sdcard::getPercentPlayed() >= 100.0 )) { buildCompleteBuzzPlayed = true; - Motherboard::getBoard().buzz(2, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); + Motherboard::getBoard().buzz(2, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, EEPROM_DEFAULT_BUZZER_REPEATS)); } bool okButtonHeld = interface::isButtonPressed(ButtonArray::OK); @@ -1422,7 +1343,9 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeString(buf); lcd.writeFromPgmspace(time_left_secs); } else if (( tsecs <= 0) || ( host::isBuildComplete()) ) { +#ifdef HAS_FILAMENT_COUNTER command::addFilamentUsed(); +#endif lcd.writeFromPgmspace(time_left_none); } else { appendTime(buf, sizeof(buf), (uint32_t)tsecs); @@ -1461,13 +1384,29 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.write('m'); break; case BUILD_TIME_PHASE_COPIES_PRINTED: - uint8_t totalCopies = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); + { + uint8_t totalCopies = eeprom::getEeprom8(eeprom::ABP_COPIES, EEPROM_DEFAULT_ABP_COPIES); lcd.setCursor(0,1); lcd.writeFromPgmspace(copies); lcd.setCursor(7,1); lcd.writeFloat((float)(copiesPrinted + 1), 0); lcd.writeFromPgmspace(of); lcd.writeFloat((float)totalCopies, 0); + } + break; + case BUILD_TIME_PHASE_ACCEL_STATS: + float minSpeed, avgSpeed, maxSpeed; + accelStatsGet(&minSpeed, &avgSpeed, &maxSpeed); + lcd.setCursor(0,1); + lcd.writeFromPgmspace(speed); + lcd.setCursor(4,1); + if ( minSpeed < 100.0 ) lcd.write(' '); //If we have space, pad out a bit + lcd.writeFloat(minSpeed,0); + lcd.write('/'); + lcd.writeFloat(avgSpeed,0); + lcd.write('/'); + lcd.writeFloat(maxSpeed,0); + lcd.write(' '); break; } @@ -1480,10 +1419,14 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if (( buildTimePhase == BUILD_TIME_PHASE_TIME_LEFT ) && ( buildDuration == 0 )) buildTimePhase = (enum BuildTimePhase)((uint8_t)buildTimePhase + 1); + //Skip acceleration stats if we're not accelerated + if (( buildTimePhase == BUILD_TIME_PHASE_ACCEL_STATS ) && ( ! steppers::isAccelerated() )) + buildTimePhase = (enum BuildTimePhase)((uint8_t)buildTimePhase + 1); + //If we're setup to print more than one copy, then show that build phase, //otherwise skip it if ( buildTimePhase == BUILD_TIME_PHASE_COPIES_PRINTED ) { - uint8_t totalCopies = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); + uint8_t totalCopies = eeprom::getEeprom8(eeprom::ABP_COPIES, EEPROM_DEFAULT_ABP_COPIES); if ( totalCopies <= 1 ) buildTimePhase = (enum BuildTimePhase)((uint8_t)buildTimePhase + 1); } @@ -1499,7 +1442,9 @@ void MonitorMode::update(LiquidCrystal& lcd, bool forceRedraw) { if (updatePhase >= UPDATE_PHASE_LAST) updatePhase = UPDATE_PHASE_FIRST; +#ifdef DEBUG_ZADVANCE steppers::doLcd(); +#endif } void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { @@ -1537,6 +1482,7 @@ void MonitorMode::notifyButtonPressed(ButtonArray::ButtonName button) { void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar version1[] = "Motherboard: _._"; const static PROGMEM prog_uchar version2[] = " Extruder: _._"; + const static PROGMEM prog_uchar version3[] = " Revision:___"; const static PROGMEM prog_uchar version4[] = "FreeSram: "; if (forceRedraw) { @@ -1548,6 +1494,9 @@ void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.setCursor(0,1); lcd.writeFromPgmspace(version2); + lcd.setCursor(0,2); + lcd.writeFromPgmspace(version3); + //Display the motherboard version lcd.setCursor(13, 0); lcd.writeInt(firmware_version / 100, 1); @@ -1571,6 +1520,9 @@ void VersionMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeString("X.X"); } + lcd.setCursor(12, 2); + lcd.writeString(STR(SVN_VERSION)); + lcd.setCursor(0,3); lcd.writeFromPgmspace(version4); lcd.writeFloat((float)StackCount(),0); @@ -1585,32 +1537,34 @@ void VersionMode::notifyButtonPressed(ButtonArray::ButtonName button) { void Menu::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar blankLine[] = " "; + uint8_t height = lcd.getDisplayHeight(); + // Do we need to redraw the whole menu? - if ((itemIndex/LCD_SCREEN_HEIGHT) != (lastDrawIndex/LCD_SCREEN_HEIGHT) + if ((itemIndex/height) != (lastDrawIndex/height) || forceRedraw ) { // Redraw the whole menu lcd.clear(); - for (uint8_t i = 0; i < LCD_SCREEN_HEIGHT; i++) { + for (uint8_t i = 0; i < height; i++) { // Instead of using lcd.clear(), clear one line at a time so there // is less screen flickr. - if (i+(itemIndex/LCD_SCREEN_HEIGHT)*LCD_SCREEN_HEIGHT +1 > itemCount) { + if (i+(itemIndex/height)*height +1 > itemCount) { break; } lcd.setCursor(1,i); // Draw one page of items at a time - drawItem(i+(itemIndex/LCD_SCREEN_HEIGHT)*LCD_SCREEN_HEIGHT, lcd); + drawItem(i+(itemIndex/height)*height, lcd); } } else { // Only need to clear the previous cursor - lcd.setCursor(0,(lastDrawIndex%LCD_SCREEN_HEIGHT)); + lcd.setCursor(0,(lastDrawIndex%height)); lcd.write(' '); } - lcd.setCursor(0,(itemIndex%LCD_SCREEN_HEIGHT)); + lcd.setCursor(0,(itemIndex%height)); lcd.write('>'); lastDrawIndex = itemIndex; } @@ -1674,7 +1628,8 @@ void Menu::notifyButtonPressed(ButtonArray::ButtonName button) { CancelBuildMenu::CancelBuildMenu() { pauseMode.autoPause = false; - itemCount = 5; + pauseMode.noheatPause = false; + itemCount = 6; reset(); pauseDisabled = false; if ( ( estimatingBuild ) || ( steppers::isHoming() ) || @@ -1688,6 +1643,7 @@ CancelBuildMenu::CancelBuildMenu() { void CancelBuildMenu::resetState() { pauseMode.autoPause = false; + pauseMode.noheatPause = false; pauseDisabled = false; if ( ( estimatingBuild ) || ( steppers::isHoming() ) || (sdcard::getPercentPlayed() >= 100.0)) pauseDisabled = true; @@ -1701,7 +1657,7 @@ void CancelBuildMenu::resetState() { itemCount = 4; } else { itemIndex = 1; - itemCount = 5; + itemCount = 6; } if ( printAnotherEnabled ) { @@ -1717,6 +1673,7 @@ void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar printAnother[] = "Print Another"; const static PROGMEM prog_uchar pauseZ[] = "Pause at ZPos "; const static PROGMEM prog_uchar pause[] = "Pause "; + const static PROGMEM prog_uchar pauseNoHeat[] = "Pause No Heat "; const static PROGMEM prog_uchar back[] = "Continue Build"; if ( ( estimatingBuild ) || ( steppers::isHoming() ) || @@ -1748,6 +1705,11 @@ void CancelBuildMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lind ++; } + if ( ! pauseDisabled ) { + if ( index == lind ) lcd.writeFromPgmspace(pauseNoHeat); + lind ++; + } + if ( index == lind ) lcd.writeFromPgmspace(back); lind ++; } @@ -1763,7 +1725,9 @@ void CancelBuildMenu::handleSelect(uint8_t index) { lind ++; if ( index == lind) { +#ifdef HAS_FILAMENT_COUNTER command::addFilamentUsed(); +#endif // Cancel build, returning to whatever menu came before monitor mode. // TODO: Cancel build. interface::popScreen(); @@ -1788,6 +1752,17 @@ void CancelBuildMenu::handleSelect(uint8_t index) { if ( index == lind ) { command::pause(true); pauseMode.autoPause = false; + pauseMode.noheatPause = false; + interface::pushScreen(&pauseMode); + } + lind ++; + } + + if ( ! pauseDisabled ) { + if ( index == lind ) { + command::pause(true); + pauseMode.autoPause = false; + pauseMode.noheatPause = true; interface::pushScreen(&pauseMode); } lind ++; @@ -1802,16 +1777,21 @@ void CancelBuildMenu::handleSelect(uint8_t index) { MainMenu::MainMenu() { itemCount = 21; +#ifdef EEPROM_MENU_ENABLE + itemCount ++; +#endif reset(); //Read in the axisStepsPerMM, we'll need these for various firmware functions later on cli(); - axisStepsPerMM[AXIS_X] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_X, STEPS_PER_MM_X_DEFAULT); - axisStepsPerMM[AXIS_Y] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Y, STEPS_PER_MM_Y_DEFAULT); - axisStepsPerMM[AXIS_Z] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Z, STEPS_PER_MM_Z_DEFAULT); - axisStepsPerMM[AXIS_A] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_A, STEPS_PER_MM_A_DEFAULT); - axisStepsPerMM[AXIS_B] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_B, STEPS_PER_MM_B_DEFAULT); + axisStepsPerMM[AXIS_X] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_X, EEPROM_DEFAULT_STEPS_PER_MM_X); + axisStepsPerMM[AXIS_Y] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Y, EEPROM_DEFAULT_STEPS_PER_MM_Y); + axisStepsPerMM[AXIS_Z] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_Z, EEPROM_DEFAULT_STEPS_PER_MM_Z); + axisStepsPerMM[AXIS_A] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_A, EEPROM_DEFAULT_STEPS_PER_MM_A); + axisStepsPerMM[AXIS_B] = eeprom::getEepromStepsPerMM(eeprom::STEPS_PER_MM_B, EEPROM_DEFAULT_STEPS_PER_MM_B); sei(); + + lcdTypeChangeTimer = 0.0; } void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { @@ -1834,8 +1814,9 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar currentPosition[]= "Position"; const static PROGMEM prog_uchar endStops[] = "Test End Stops"; const static PROGMEM prog_uchar stepsPerMm[] = "Axis Steps:mm"; + const static PROGMEM prog_uchar homingRates[] = "Homing Rates"; const static PROGMEM prog_uchar versions[] = "Version"; - const static PROGMEM prog_uchar snake[] = "Snake Game"; + const static PROGMEM prog_uchar eeprom[] = "Eeprom"; switch (index) { case 0: @@ -1896,14 +1877,20 @@ void MainMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(stepsPerMm); break; case 19: - lcd.writeFromPgmspace(versions); + lcd.writeFromPgmspace(homingRates); break; case 20: - lcd.writeFromPgmspace(snake); + lcd.writeFromPgmspace(versions); break; +#ifdef EEPROM_MENU_ENABLE + case 21: + lcd.writeFromPgmspace(eeprom); + break; +#endif } } + void MainMenu::handleSelect(uint8_t index) { switch (index) { case 0: @@ -1984,18 +1971,38 @@ void MainMenu::handleSelect(uint8_t index) { interface::pushScreen(&stepsPerMMMode); break; case 19: - // Show build from SD screen - interface::pushScreen(&versionMode); + // Show Homing Rates Menu + interface::pushScreen(&homingFeedRatesMode); break; case 20: // Show build from SD screen - interface::pushScreen(&snake); + interface::pushScreen(&versionMode); break; +#ifdef EEPROM_MENU_ENABLE + case 21: + //Eeprom Menu + interface::pushScreen(&eepromMenu); + break; +#endif } } -SDMenu::SDMenu() { - reset(); +void MainMenu::update(LiquidCrystal& lcd, bool forceRedraw) { + Menu::update(lcd, forceRedraw); + + if (interface::isButtonPressed(ButtonArray::XMINUS)) { + if (( lcdTypeChangeTimer != -1.0 ) && ( lcdTypeChangeTimer + LCD_TYPE_CHANGE_BUTTON_HOLD_TIME ) <= Motherboard::getBoard().getCurrentSeconds()) { + Motherboard::getBoard().buzz(1, 1, 1); + lcdTypeChangeTimer = -1.0; + lcd.nextLcdType(); + lcd.reloadDisplayType(); + host::stopBuild(); + } + } else lcdTypeChangeTimer = Motherboard::getBoard().getCurrentSeconds(); +} + +SDMenu::SDMenu() { + reset(); updatePhase = 0; drawItemLockout = false; } @@ -2090,13 +2097,13 @@ void SDMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { uint8_t idx; uint8_t longFilenameOffset = 0; - uint8_t displayWidth = LCD_SCREEN_WIDTH - 1; + uint8_t displayWidth = lcd.getDisplayWidth() - 1; //Support scrolling filenames that are longer than the lcd screen if (filenameLength >= displayWidth) longFilenameOffset = updatePhase % (filenameLength - displayWidth + 1); - for (idx = 0; (idx < displayWidth) && (fnbuf[longFilenameOffset + idx] != 0) && - ((longFilenameOffset + idx) < MAX_FILE_LEN); idx++) + for (idx = 0; (idx < displayWidth) && ((longFilenameOffset + idx) < MAX_FILE_LEN) && + (fnbuf[longFilenameOffset + idx] != 0); idx++) lcd.write(fnbuf[longFilenameOffset + idx]); //Clear out the rest of the line @@ -2107,17 +2114,18 @@ void SDMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { } void SDMenu::update(LiquidCrystal& lcd, bool forceRedraw) { + uint8_t height = lcd.getDisplayHeight(); if (( ! forceRedraw ) && ( ! drawItemLockout )) { //Redraw the last item if we have changed - if (((itemIndex/LCD_SCREEN_HEIGHT) == (lastDrawIndex/LCD_SCREEN_HEIGHT)) && + if (((itemIndex/height) == (lastDrawIndex/height)) && ( itemIndex != lastItemIndex )) { - lcd.setCursor(1,lastItemIndex % LCD_SCREEN_HEIGHT); + lcd.setCursor(1,lastItemIndex % height); drawItem(lastItemIndex, lcd); } lastItemIndex = itemIndex; - lcd.setCursor(1,itemIndex % LCD_SCREEN_HEIGHT); + lcd.setCursor(1,itemIndex % height); drawItem(itemIndex, lcd); } @@ -2157,87 +2165,18 @@ void SDMenu::handleSelect(uint8_t index) { } } - -void Tool0TempSetScreen::reset() { - value = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220);; -} - -void Tool0TempSetScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar message1[] = "Tool0 Targ Temp:"; - const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; - - if (forceRedraw) { - lcd.clear(); - - lcd.setCursor(0,0); - lcd.writeFromPgmspace(message1); - - lcd.setCursor(0,3); - lcd.writeFromPgmspace(message4); - } - - - // Redraw tool info - lcd.setCursor(0,1); - lcd.writeInt(value,3); -} - -void Tool0TempSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) { - switch (button) { - case ButtonArray::CANCEL: - interface::popScreen(); - break; - case ButtonArray::ZERO: - break; - case ButtonArray::OK: - eeprom_write_byte((uint8_t*)eeprom::TOOL0_TEMP,value); - interface::popScreen(); - break; - case ButtonArray::ZPLUS: - // increment more - if (value <= 250) { - value += 5; - } - break; - case ButtonArray::ZMINUS: - // decrement more - if (value >= 5) { - value -= 5; - } - break; - case ButtonArray::YPLUS: - // increment less - if (value <= 254) { - value += 1; - } - break; - case ButtonArray::YMINUS: - // decrement less - if (value >= 1) { - value -= 1; - } - break; - - case ButtonArray::XMINUS: - case ButtonArray::XPLUS: - break; - } -} - - -void PlatformTempSetScreen::reset() { - value = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110);; +void ValueSetScreen::reset() { + value = eeprom::getEeprom8(location, default_value); } -void PlatformTempSetScreen::update(LiquidCrystal& lcd, bool forceRedraw) { - const static PROGMEM prog_uchar message1[] = "Bed Target Temp:"; +void ValueSetScreen::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; if (forceRedraw) { lcd.clear(); lcd.setCursor(0,0); - lcd.writeFromPgmspace(message1); + lcd.writeString(message1); lcd.setCursor(0,3); lcd.writeFromPgmspace(message4); @@ -2247,9 +2186,10 @@ void PlatformTempSetScreen::update(LiquidCrystal& lcd, bool forceRedraw) { // Redraw tool info lcd.setCursor(0,1); lcd.writeInt(value,3); + if ( units ) lcd.writeString(units); } -void PlatformTempSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) { +void ValueSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) { switch (button) { case ButtonArray::CANCEL: interface::popScreen(); @@ -2257,30 +2197,30 @@ void PlatformTempSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) case ButtonArray::ZERO: break; case ButtonArray::OK: - eeprom_write_byte((uint8_t*)eeprom::PLATFORM_TEMP,value); + eeprom_write_byte((uint8_t*)location,value); interface::popScreen(); break; case ButtonArray::ZPLUS: // increment more - if (value <= 250) { + if (value <= 249) { value += 5; } break; case ButtonArray::ZMINUS: // decrement more - if (value >= 5) { + if (value >= 6) { value -= 5; } break; case ButtonArray::YPLUS: // increment less - if (value <= 254) { + if (value <= 253) { value += 1; } break; case ButtonArray::YMINUS: // decrement less - if (value >= 1) { + if (value >= 2) { value -= 1; } break; @@ -2291,7 +2231,6 @@ void PlatformTempSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) } } - PreheatMenu::PreheatMenu() { itemCount = 4; reset(); @@ -2350,7 +2289,7 @@ void PreheatMenu::handleSelect(uint8_t index) { if (tool0Temp > 0) { extruderControl(SLAVE_CMD_SET_TEMP, EXTDR_CMD_SET, responsePacket, 0); } else { - uint8_t value = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220); + uint8_t value = eeprom::getEeprom8(eeprom::TOOL0_TEMP, EEPROM_DEFAULT_TOOL0_TEMP); extruderControl(SLAVE_CMD_SET_TEMP, EXTDR_CMD_SET, responsePacket, (uint16_t)value); } fetchTargetTemps(); @@ -2361,7 +2300,7 @@ void PreheatMenu::handleSelect(uint8_t index) { if (platformTemp > 0) { extruderControl(SLAVE_CMD_SET_PLATFORM_TEMP, EXTDR_CMD_SET, responsePacket, 0); } else { - uint8_t value = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110); + uint8_t value = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, EEPROM_DEFAULT_PLATFORM_TEMP); extruderControl(SLAVE_CMD_SET_PLATFORM_TEMP, EXTDR_CMD_SET, responsePacket, value); } fetchTargetTemps(); @@ -2369,11 +2308,19 @@ void PreheatMenu::handleSelect(uint8_t index) { break; case 2: // Show Extruder Temperature Setting Screen - interface::pushScreen(&tool0TempSetScreen); + heaterTempSetScreen.location = eeprom::TOOL0_TEMP; + heaterTempSetScreen.default_value = EEPROM_DEFAULT_TOOL0_TEMP; + heaterTempSetScreen.message1 = "Tool0 Targ Temp:"; + heaterTempSetScreen.units = NULL; + interface::pushScreen(&heaterTempSetScreen); break; case 3: // Show Platform Temperature Setting Screen - interface::pushScreen(&platTempSetScreen); + heaterTempSetScreen.location = eeprom::PLATFORM_TEMP; + heaterTempSetScreen.default_value = EEPROM_DEFAULT_PLATFORM_TEMP; + heaterTempSetScreen.message1 = "Bed Target Temp:"; + heaterTempSetScreen.units = NULL; + interface::pushScreen(&heaterTempSetScreen); break; } } @@ -2384,7 +2331,7 @@ void HomeAxisMode::reset() { void HomeAxisMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar home1[] = "Home Axis: "; const static PROGMEM prog_uchar home2[] = " Y Z"; - const static PROGMEM prog_uchar home3[] = "X X "; + const static PROGMEM prog_uchar home3[] = "X X (endstops)"; const static PROGMEM prog_uchar home4[] = " Y Z"; if (forceRedraw) { @@ -2404,32 +2351,40 @@ void HomeAxisMode::update(LiquidCrystal& lcd, bool forceRedraw) { } void HomeAxisMode::home(ButtonArray::ButtonName direction) { - uint8_t axis = 0; - bool maximums; - float interval = 2000.0; + uint8_t axis = 0, axisIndex = 0; + bool maximums = false; + + uint8_t endstops = eeprom::getEeprom8(eeprom::ENDSTOPS_USED, EEPROM_DEFAULT_ENDSTOPS_USED); switch(direction) { case ButtonArray::XMINUS: case ButtonArray::XPLUS: axis = 0x01; - maximums = false; - interval *= stepsToMM((int32_t)47.06, AXIS_X); //Use ToM as baseline + if ( endstops & 0x02 ) maximums = true; + if ( endstops & 0x01 ) maximums = false; + axisIndex = 0; break; case ButtonArray::YMINUS: case ButtonArray::YPLUS: axis = 0x02; - maximums = false; - interval *= stepsToMM((int32_t)47.06, AXIS_Y); //Use ToM as baseline + if ( endstops & 0x08 ) maximums = true; + if ( endstops & 0x04 ) maximums = false; + axisIndex = 1; break; case ButtonArray::ZMINUS: case ButtonArray::ZPLUS: axis = 0x04; - maximums = true; - interval /= 4.0; //Speed up Z - interval *= stepsToMM((int32_t)200.0, AXIS_Z); //Use ToM as baseline + if ( endstops & 0x20 ) maximums = true; + if ( endstops & 0x10 ) maximums = false; + axisIndex = 2; break; } + //60.0, because feed rate is in mm/min units, we convert to seconds + float feedRate = (float)eeprom::getEepromUInt32(eeprom::HOMING_FEED_RATE_X + (axisIndex * sizeof(uint32_t)), 500) / 60.0; + float stepsPerSecond = feedRate * (float)mmToSteps(1.0, (enum Axis)axisIndex); + int32_t interval = (int32_t)(1000000.0 / stepsPerSecond); + steppers::startHoming(maximums, axis, (uint32_t)interval); } @@ -2445,6 +2400,8 @@ void HomeAxisMode::notifyButtonPressed(ButtonArray::ButtonName button) { break; case ButtonArray::ZERO: case ButtonArray::OK: + interface::pushScreen(&endStopConfigScreen); + break; case ButtonArray::CANCEL: steppers::abort(); steppers::enableAxis(0, false); @@ -2455,30 +2412,29 @@ void HomeAxisMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } -SteppersMenu::SteppersMenu() { +EnabledDisabledMenu::EnabledDisabledMenu() { itemCount = 4; reset(); } -void SteppersMenu::resetState() { - if (( steppers::isEnabledAxis(0) ) || - ( steppers::isEnabledAxis(1) ) || - ( steppers::isEnabledAxis(2) ) || - ( steppers::isEnabledAxis(3) )) itemIndex = 3; - else itemIndex = 2; +void EnabledDisabledMenu::resetState() { + setupTitle(); + + if ( isEnabled() ) itemIndex = 3; + else itemIndex = 2; firstItemIndex = 2; } -void SteppersMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar title[] = "Stepper Motors:"; +void EnabledDisabledMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar disable[] = "Disable"; const static PROGMEM prog_uchar enable[] = "Enable"; switch (index) { case 0: - lcd.writeFromPgmspace(title); + lcd.writeString(msg1); break; case 1: + if ( msg2 ) lcd.writeString(msg2); break; case 2: lcd.writeFromPgmspace(disable); @@ -2489,25 +2445,30 @@ void SteppersMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { } } -void SteppersMenu::handleSelect(uint8_t index) { - switch (index) { - case 2: - //Disable Steppers - steppers::enableAxis(0, false); - steppers::enableAxis(1, false); - steppers::enableAxis(2, false); - steppers::enableAxis(3, false); - interface::popScreen(); - break; - case 3: - //Enable Steppers - steppers::enableAxis(0, true); - steppers::enableAxis(1, true); - steppers::enableAxis(2, true); - steppers::enableAxis(3, true); - interface::popScreen(); - break; - } +void EnabledDisabledMenu::handleSelect(uint8_t index) { + if ( index == 2 ) enable(false); + if ( index == 3 ) enable(true); + interface::popScreen(); +} + +bool SteppersMenu::isEnabled() { + if (( steppers::isEnabledAxis(0) ) || + ( steppers::isEnabledAxis(1) ) || + ( steppers::isEnabledAxis(2) ) || + ( steppers::isEnabledAxis(3) )) return true; + return false; +} + +void SteppersMenu::enable(bool enabled) { + steppers::enableAxis(0, enabled); + steppers::enableAxis(1, enabled); + steppers::enableAxis(2, enabled); + steppers::enableAxis(3, enabled); +} + +void SteppersMenu::setupTitle() { + msg1 = "Stepper Motors:"; + msg2 = NULL; } void TestEndStopsMode::reset() { @@ -2515,9 +2476,9 @@ void TestEndStopsMode::reset() { void TestEndStopsMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar test1[] = "Test End Stops: "; - const static PROGMEM prog_uchar test2[] = "(press end stop)"; - const static PROGMEM prog_uchar test3[] = "XMin:N YMin:N"; - const static PROGMEM prog_uchar test4[] = "ZMax:N"; + const static PROGMEM prog_uchar test2[] = "XMin:N XMax:N"; + const static PROGMEM prog_uchar test3[] = "YMin:N YMax:N"; + const static PROGMEM prog_uchar test4[] = "ZMin:N ZMax:N"; const static PROGMEM prog_uchar strY[] = "Y"; const static PROGMEM prog_uchar strN[] = "N"; @@ -2536,15 +2497,24 @@ void TestEndStopsMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeFromPgmspace(test4); } - lcd.setCursor(5, 2); + lcd.setCursor(5, 1); if ( steppers::isAtMinimum(0) ) lcd.writeFromPgmspace(strY); else lcd.writeFromPgmspace(strN); + lcd.setCursor(15, 1); + if ( steppers::isAtMaximum(0) ) lcd.writeFromPgmspace(strY); + else lcd.writeFromPgmspace(strN); - lcd.setCursor(15, 2); + lcd.setCursor(5, 2); if ( steppers::isAtMinimum(1) ) lcd.writeFromPgmspace(strY); else lcd.writeFromPgmspace(strN); + lcd.setCursor(15, 2); + if ( steppers::isAtMaximum(1) ) lcd.writeFromPgmspace(strY); + else lcd.writeFromPgmspace(strN); lcd.setCursor(5, 3); + if ( steppers::isAtMinimum(2) ) lcd.writeFromPgmspace(strY); + else lcd.writeFromPgmspace(strN); + lcd.setCursor(15, 3); if ( steppers::isAtMaximum(2) ) lcd.writeFromPgmspace(strY); else lcd.writeFromPgmspace(strN); } @@ -2568,65 +2538,15 @@ void TestEndStopsMode::notifyButtonPressed(ButtonArray::ButtonName button) { void PauseMode::reset() { pauseState = 0; lastDirectionButtonPressed = (ButtonArray::ButtonName)0; + userViewMode = eeprom::getEeprom8(eeprom::JOG_MODE_SETTINGS, EEPROM_DEFAULT_JOG_MODE_SETTINGS) & 0x01; } -void PauseMode::jog(ButtonArray::ButtonName direction) { - bool extrude = false; - int32_t interval = 1000; - float speed = 1.5; //In mm's - Point position = steppers::getPosition(); - - switch(direction) { - case ButtonArray::XMINUS: - position[0] -= mmToSteps(speed, AXIS_X); - break; - case ButtonArray::XPLUS: - position[0] += mmToSteps(speed, AXIS_X); - break; - case ButtonArray::YMINUS: - position[1] -= mmToSteps(speed, AXIS_Y); - break; - case ButtonArray::YPLUS: - position[1] += mmToSteps(speed, AXIS_Y); - break; - case ButtonArray::ZMINUS: - position[2] -= mmToSteps(speed, AXIS_Z); - break; - case ButtonArray::ZPLUS: - position[2] += mmToSteps(speed, AXIS_Z); - break; - case ButtonArray::OK: - case ButtonArray::ZERO: - float rpm = (float)eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19) / 10.0; - - //60 * 1000000 = # uS in a minute - //200 * 8 = 200 steps per revolution * 1/8 stepping - interval = (int32_t)(60L * 1000000L) / (int32_t)((float)(200 * 8) * rpm); - int16_t stepsPerSecond = (int16_t)((200.0 * 8.0 * rpm) / 60.0); - - //50.235479 is ToM stepper extruder speed, we - //use this as a baseline - stepsPerSecond = (int16_t)((float)stepsPerSecond * stepsToMM((int32_t)50.235479, AXIS_A)); - - //Handle reverse - if ( direction == ButtonArray::OK ) stepsPerSecond *= -1; - - //Extrude for 0.5 seconds - position[3] += 0.5 * stepsPerSecond; - break; - } - - lastDirectionButtonPressed = direction; - - steppers::setTarget(position, interval); -} - - void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar waitForCurrentCommand[] = "Entering pause.."; const static PROGMEM prog_uchar retractFilament[] = "Retract Filament"; const static PROGMEM prog_uchar movingZ[] = "Moving Z up 2mm "; const static PROGMEM prog_uchar movingY[] = "Moving Y 2mm "; + const static PROGMEM prog_uchar heating[] = "Heating... "; const static PROGMEM prog_uchar leavingPaused[] = "Leaving pause.. "; const static PROGMEM prog_uchar paused1[] = "Paused("; const static PROGMEM prog_uchar paused2[] = " Y+ Z+"; @@ -2634,31 +2554,39 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar paused4[] = " Y- Z-"; int32_t interval = 1000; - Point newPosition = pausedPosition; + + Point newPosition = Point(0,0,0,0,0); if (forceRedraw) lcd.clear(); + OutPacket responsePacket; + lcd.setCursor(0,0); switch (pauseState) { case 0: //Entered pause, waiting for steppers to finish last command lcd.writeFromPgmspace(waitForCurrentCommand); - steppers::switchToRegularDriver(); + steppers::switchToRegularDriver(true); if ( ! steppers::isRunning()) pauseState ++; break; case 1: //Last command finished, record current position and + { //retract filament lcd.writeFromPgmspace(retractFilament); pausedPosition = steppers::getPosition(); newPosition = pausedPosition; - newPosition[3] += mmToSteps(1.0, AXIS_A); //Retract the filament so we don't get blobs + //Handle 5D + float retractAmount = 1.0; + if ( eeprom::getEeprom8(eeprom::INVERTED_EXTRUDER_5D, EEPROM_DEFAULT_INVERTED_EXTRUDER_5D) == 1 ) retractAmount *= -1.0; + newPosition[3] += mmToSteps(retractAmount, AXIS_A); //Retract the filament so we don't get blobs steppers::setTarget(newPosition, interval / 2); pauseState ++; + } break; case 2: //Wait for the retract to complete @@ -2670,8 +2598,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { case 3: //Last command finished, record position and move Y to dislodge filament lcd.writeFromPgmspace(movingY); - pausedPosition = steppers::getPosition(); - newPosition = pausedPosition; + newPosition = steppers::getPosition(); newPosition[1] += mmToSteps(4.0, AXIS_Y); steppers::setTarget(newPosition, interval / 2); @@ -2715,36 +2642,91 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { lcd.writeFromPgmspace(paused4); } break; + + case 7: //If noheatPause, then shutdown the heaters + if ( noheatPause ) { + //Get the tool set point and the hbp set point for later reheating + tool0SetPoint = 0; + if (extruderControl(SLAVE_CMD_GET_SP, EXTDR_CMD_GET, responsePacket, 0)) + tool0SetPoint = responsePacket.read16(1); + + hbpSetPoint = 0; + if (extruderControl(SLAVE_CMD_GET_PLATFORM_SP, EXTDR_CMD_GET, responsePacket, 0)); + hbpSetPoint = responsePacket.read16(1); + + //Kill the heaters + extruderControl(SLAVE_CMD_SET_TEMP, EXTDR_CMD_SET, responsePacket, (uint16_t)0); + extruderControl(SLAVE_CMD_SET_PLATFORM_TEMP, EXTDR_CMD_SET, responsePacket, (uint16_t)0); + + //Kill the fan + extruderControl(SLAVE_CMD_TOGGLE_FAN, EXTDR_CMD_SET, responsePacket, 0); + } + pauseState ++; + break; - case 7: //Buzz if we're a Pause@ZPos - if ( autoPause ) Motherboard::getBoard().buzz(4, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3)); + case 8: //Buzz if we're a Pause@ZPos + if ( autoPause ) Motherboard::getBoard().buzz(4, 3, eeprom::getEeprom8(eeprom::BUZZER_REPEATS, EEPROM_DEFAULT_BUZZER_REPEATS)); pauseState ++; break; - case 8: //We're now paused + case 9: //We're now paused + break; + + case 10: //If noheatPause, then startup the heaters + if ( noheatPause ) { + //Restart the heaters and fan + lcd.clear(); + lcd.writeFromPgmspace(heating); + extruderControl(SLAVE_CMD_SET_TEMP, EXTDR_CMD_SET, responsePacket, (uint16_t)tool0SetPoint); + extruderControl(SLAVE_CMD_SET_PLATFORM_TEMP, EXTDR_CMD_SET, responsePacket, (uint16_t)hbpSetPoint); + extruderControl(SLAVE_CMD_TOGGLE_FAN, EXTDR_CMD_SET, responsePacket, 1); + } + pauseState ++; + break; + + case 11: //If noheatPause, then wait for the tool0 to reach temp + if ( noheatPause ) { + if (extruderControl(SLAVE_CMD_IS_TOOL_READY, EXTDR_CMD_GET, responsePacket, 0)) { + uint8_t ready = responsePacket.read8(1); + if ( ready ) pauseState ++; + } + } + else pauseState ++; break; - case 9: //Leaving paused, wait for any steppers to finish + case 12: //If noheatPause, then wait for the hbp to reach temp + if ( noheatPause ) { + if (extruderControl(SLAVE_CMD_IS_PLATFORM_READY, EXTDR_CMD_GET, responsePacket, 0)) { + uint8_t ready = responsePacket.read8(1); + if ( ready ) pauseState ++; + } + } + else pauseState ++; + break; + + case 13: //Leaving paused, wait for any steppers to finish if ( autoPause ) command::pauseAtZPos(0); lcd.clear(); lcd.writeFromPgmspace(leavingPaused); if ( ! steppers::isRunning()) pauseState ++; break; - case 10://Return to original position + case 14://Return to original position lcd.writeFromPgmspace(leavingPaused); //The extruders may have moved, so it doesn't make sense //to go back to the old position, or we'll eject the filament + //However because 5D uses absolute positioning + //the best way to solve this is to use definePosition for the extruder + //axis newPosition = steppers::getPosition(); - pausedPosition[3] = newPosition[3]; - pausedPosition[4] = newPosition[4]; + steppers::definePosition(Point(newPosition[0],newPosition[1],newPosition[2],pausedPosition[3],pausedPosition[4])); steppers::setTarget(pausedPosition, interval); pauseState ++; break; - case 11://Wait for return to original position + case 15://Wait for return to original position lcd.writeFromPgmspace(leavingPaused); if ( ! steppers::isRunning()) { pauseState = 0; @@ -2758,7 +2740,7 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( lastDirectionButtonPressed ) { if (interface::isButtonPressed(lastDirectionButtonPressed)) - jog(lastDirectionButtonPressed); + jog(lastDirectionButtonPressed, true); else { lastDirectionButtonPressed = (ButtonArray::ButtonName)0; steppers::abort(); @@ -2767,9 +2749,9 @@ void PauseMode::update(LiquidCrystal& lcd, bool forceRedraw) { } void PauseMode::notifyButtonPressed(ButtonArray::ButtonName button) { - if ( pauseState == 8 ) { + if ( pauseState == 9 ) { if ( button == ButtonArray::CANCEL ) pauseState ++; - else jog(button); + else jog(button, true); } } @@ -2977,13 +2959,23 @@ void CalibrateMode::update(LiquidCrystal& lcd, bool forceRedraw) { uint8_t axes; float interval = 2000.0; + bool maximums = false; + + uint8_t endstops = eeprom::getEeprom8(eeprom::ENDSTOPS_USED, EEPROM_DEFAULT_ENDSTOPS_USED); + + float feedRate, stepsPerSecond; switch(calibrationState) { case CS_HOME_Z: //Declare current position to be x=0, y=0, z=0, a=0, b=0 steppers::definePosition(Point(0,0,0,0,0)); interval *= stepsToMM((int32_t)200.0, AXIS_Z); //Use ToM as baseline - steppers::startHoming(true, 0x04, (uint32_t)interval); + if ( endstops & 0x20 ) maximums = true; + if ( endstops & 0x10 ) maximums = false; + feedRate = (float)eeprom::getEepromUInt32(eeprom::HOMING_FEED_RATE_Z, EEPROM_DEFAULT_HOMING_FEED_RATE_Z) / 60.0; + stepsPerSecond = feedRate * (float)mmToSteps(1.0, AXIS_Z); + interval = 1000000.0 / stepsPerSecond; + steppers::startHoming(maximums, 0x04, (uint32_t)interval); calibrationState = CS_HOME_Z_WAIT; break; case CS_HOME_Z_WAIT: @@ -2991,7 +2983,12 @@ void CalibrateMode::update(LiquidCrystal& lcd, bool forceRedraw) { break; case CS_HOME_Y: interval *= stepsToMM((int32_t)47.06, AXIS_Y); //Use ToM as baseline - steppers::startHoming(false, 0x02, (uint32_t)interval); + if ( endstops & 0x08 ) maximums = true; + if ( endstops & 0x04 ) maximums = false; + feedRate = (float)eeprom::getEepromUInt32(eeprom::HOMING_FEED_RATE_Y, EEPROM_DEFAULT_HOMING_FEED_RATE_Y) / 60.0; + stepsPerSecond = feedRate * (float)mmToSteps(1.0, AXIS_Y); + interval = 1000000.0 / stepsPerSecond; + steppers::startHoming(maximums, 0x02, (uint32_t)interval); calibrationState = CS_HOME_Y_WAIT; break; case CS_HOME_Y_WAIT: @@ -2999,7 +2996,12 @@ void CalibrateMode::update(LiquidCrystal& lcd, bool forceRedraw) { break; case CS_HOME_X: interval *= stepsToMM((int32_t)47.06, AXIS_X); //Use ToM as baseline - steppers::startHoming(false, 0x01, (uint32_t)interval); + if ( endstops & 0x02 ) maximums = true; + if ( endstops & 0x01 ) maximums = false; + feedRate = (float)eeprom::getEepromUInt32(eeprom::HOMING_FEED_RATE_X, EEPROM_DEFAULT_HOMING_FEED_RATE_X) / 60.0; + stepsPerSecond = feedRate * (float)mmToSteps(1.0, AXIS_X); + interval = 1000000.0 / stepsPerSecond; + steppers::startHoming(maximums, 0x01, (uint32_t)interval); calibrationState = CS_HOME_X_WAIT; break; case CS_HOME_X_WAIT: @@ -3166,7 +3168,7 @@ void HomeOffsetsMode::notifyButtonPressed(ButtonArray::ButtonName button) { } void BuzzerSetRepeatsMode::reset() { - repeats = eeprom::getEeprom8(eeprom::BUZZER_REPEATS, 3); + repeats = eeprom::getEeprom8(eeprom::BUZZER_REPEATS, EEPROM_DEFAULT_BUZZER_REPEATS); } void BuzzerSetRepeatsMode::update(LiquidCrystal& lcd, bool forceRedraw) { @@ -3227,51 +3229,20 @@ void BuzzerSetRepeatsMode::notifyButtonPressed(ButtonArray::ButtonName button) { } } -ExtruderFanMenu::ExtruderFanMenu() { - itemCount = 4; - reset(); +bool ExtruderFanMenu::isEnabled() { + //Should really check the current status of the fan here + return false; } -void ExtruderFanMenu::resetState() { - itemIndex = 2; - firstItemIndex = 2; -} - -void ExtruderFanMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar title[] = "Extruder Fan:"; - const static PROGMEM prog_uchar off[] = "Off"; - const static PROGMEM prog_uchar on[] = "On"; +void ExtruderFanMenu::enable(bool enabled) { + OutPacket responsePacket; - switch (index) { - case 0: - lcd.writeFromPgmspace(title); - break; - case 1: - break; - case 2: - lcd.writeFromPgmspace(off); - break; - case 3: - lcd.writeFromPgmspace(on); - break; - } + extruderControl(SLAVE_CMD_TOGGLE_FAN, EXTDR_CMD_SET, responsePacket, (enabled)?1:0); } -void ExtruderFanMenu::handleSelect(uint8_t index) { - OutPacket responsePacket; - - switch (index) { - case 2: - //Disable Cooling Fan - extruderControl(SLAVE_CMD_TOGGLE_FAN, EXTDR_CMD_SET, responsePacket, 0); - interface::popScreen(); - break; - case 3: - //Enable Cooling Fan - extruderControl(SLAVE_CMD_TOGGLE_FAN, EXTDR_CMD_SET, responsePacket, 1); - interface::popScreen(); - break; - } +void ExtruderFanMenu::setupTitle() { + msg1 = "Extruder Fan:"; + msg2 = NULL; } void StepsPerMMMode::reset() { @@ -3458,8 +3429,8 @@ void FilamentUsedResetMenu::handleSelect(uint8_t index) { switch (index) { case 3: //Reset to zero - eeprom::putEepromInt64(eeprom::FILAMENT_USED, 0); - eeprom::putEepromInt64(eeprom::FILAMENT_USED_TRIP, 0); + eeprom::putEepromInt64(eeprom::FILAMENT_USED, EEPROM_DEFAULT_FILAMENT_USED); + eeprom::putEepromInt64(eeprom::FILAMENT_USED_TRIP, EEPROM_DEFAULT_FILAMENT_USED_TRIP); case 2: interface::popScreen(); interface::popScreen(); @@ -3485,10 +3456,10 @@ void FilamentUsedMode::update(LiquidCrystal& lcd, bool forceRedraw) { if ( lifetimeDisplay ) lcd.writeFromPgmspace(lifetime); else lcd.writeFromPgmspace(trip); - int64_t filamentUsed = eeprom::getEepromInt64(eeprom::FILAMENT_USED, 0); + int64_t filamentUsed = eeprom::getEepromInt64(eeprom::FILAMENT_USED, EEPROM_DEFAULT_FILAMENT_USED); if ( ! lifetimeDisplay ) { - int64_t trip = eeprom::getEepromInt64(eeprom::FILAMENT_USED_TRIP, 0); + int64_t trip = eeprom::getEepromInt64(eeprom::FILAMENT_USED_TRIP, EEPROM_DEFAULT_FILAMENT_USED_TRIP); filamentUsed = filamentUsed - trip; } @@ -3523,7 +3494,7 @@ void FilamentUsedMode::notifyButtonPressed(ButtonArray::ButtonName button) { if ( lifetimeDisplay ) interface::pushScreen(&filamentUsedResetMenu); else { - eeprom::putEepromInt64(eeprom::FILAMENT_USED_TRIP, eeprom::getEepromInt64(eeprom::FILAMENT_USED, 0)); + eeprom::putEepromInt64(eeprom::FILAMENT_USED_TRIP, eeprom::getEepromInt64(eeprom::FILAMENT_USED, EEPROM_DEFAULT_FILAMENT_USED)); interface::popScreen(); } break; @@ -3538,7 +3509,7 @@ void FilamentUsedMode::notifyButtonPressed(ButtonArray::ButtonName button) { } BuildSettingsMenu::BuildSettingsMenu() { - itemCount = 4; + itemCount = 5; reset(); } @@ -3552,6 +3523,7 @@ void BuildSettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar item2[] = "Override Temp"; const static PROGMEM prog_uchar item3[] = "ABP Copies (SD)"; const static PROGMEM prog_uchar item4[] = "Acceleration"; + const static PROGMEM prog_uchar item5[] = "5D Extruder"; switch (index) { case 0: @@ -3566,6 +3538,9 @@ void BuildSettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { case 3: lcd.writeFromPgmspace(item4); break; + case 4: + lcd.writeFromPgmspace(item5); + break; } } @@ -3589,14 +3564,18 @@ void BuildSettingsMenu::handleSelect(uint8_t index) { //Acceleration menu interface::pushScreen(&accelerationMenu); break; + case 4: + //5D Extruder Menu + interface::pushScreen(&extruder5DMenu); + break; } } void ABPCopiesSetScreen::reset() { - value = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); + value = eeprom::getEeprom8(eeprom::ABP_COPIES, EEPROM_DEFAULT_ABP_COPIES); if ( value < 1 ) { - eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,1); - value = eeprom::getEeprom8(eeprom::ABP_COPIES, 1); //Just in case + eeprom_write_byte((uint8_t*)eeprom::ABP_COPIES,EEPROM_DEFAULT_ABP_COPIES); + value = eeprom::getEeprom8(eeprom::ABP_COPIES, EEPROM_DEFAULT_ABP_COPIES); //Just in case } } @@ -3662,117 +3641,69 @@ void ABPCopiesSetScreen::notifyButtonPressed(ButtonArray::ButtonName button) { } } -PreheatDuringEstimateMenu::PreheatDuringEstimateMenu() { - itemCount = 4; - reset(); +bool PreheatDuringEstimateMenu::isEnabled() { + if ( eeprom::getEeprom8(eeprom::PREHEAT_DURING_ESTIMATE, EEPROM_DEFAULT_PREHEAT_DURING_ESTIMATE) ) return true; + return false; } -void PreheatDuringEstimateMenu::resetState() { - if ( eeprom::getEeprom8(eeprom::PREHEAT_DURING_ESTIMATE, 0) ) itemIndex = 3; - else itemIndex = 2; - firstItemIndex = 2; +void PreheatDuringEstimateMenu::enable(bool enabled) { + eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,(enabled)?1:0); } -void PreheatDuringEstimateMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar msg1[] = "Preheat during"; - const static PROGMEM prog_uchar msg2[] = "estimate phase:"; - const static PROGMEM prog_uchar disable[] = "Disable"; - const static PROGMEM prog_uchar enable[] = "Enable"; - - switch (index) { - case 0: - lcd.writeFromPgmspace(msg1); - break; - case 1: - lcd.writeFromPgmspace(msg2); - break; - case 2: - lcd.writeFromPgmspace(disable); - break; - case 3: - lcd.writeFromPgmspace(enable); - break; - } +void PreheatDuringEstimateMenu::setupTitle() { + msg1 = "Preheat during"; + msg2 = "estimate phase:"; } -void PreheatDuringEstimateMenu::handleSelect(uint8_t index) { - switch (index) { - case 2: - eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,0); - interface::popScreen(); - break; - case 3: - eeprom_write_byte((uint8_t*)eeprom::PREHEAT_DURING_ESTIMATE,1); - interface::popScreen(); - break; - } +bool OverrideGCodeTempMenu::isEnabled() { + if ( eeprom::getEeprom8(eeprom::OVERRIDE_GCODE_TEMP, EEPROM_DEFAULT_OVERRIDE_GCODE_TEMP) ) return true; + return false; } -OverrideGCodeTempMenu::OverrideGCodeTempMenu() { - itemCount = 4; - reset(); +void OverrideGCodeTempMenu::enable(bool enabled) { + eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,(enabled)?1:0); } -void OverrideGCodeTempMenu::resetState() { - if ( eeprom::getEeprom8(eeprom::OVERRIDE_GCODE_TEMP, 0) ) itemIndex = 3; - else itemIndex = 2; - firstItemIndex = 2; +void OverrideGCodeTempMenu::setupTitle() { + msg1 = "Override GCode"; + msg2 = "Temperature:"; } -void OverrideGCodeTempMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar msg1[] = "Override GCode"; - const static PROGMEM prog_uchar msg2[] = "Temperature:"; - const static PROGMEM prog_uchar disable[] = "Disable"; - const static PROGMEM prog_uchar enable[] = "Enable"; +bool Extruder5DMenu::isEnabled() { + if ( eeprom::getEeprom8(eeprom::INVERTED_EXTRUDER_5D, EEPROM_DEFAULT_INVERTED_EXTRUDER_5D) == 1 ) return true; + return false; +} - switch (index) { - case 0: - lcd.writeFromPgmspace(msg1); - break; - case 1: - lcd.writeFromPgmspace(msg2); - break; - case 2: - lcd.writeFromPgmspace(disable); - break; - case 3: - lcd.writeFromPgmspace(enable); - break; - } +void Extruder5DMenu::enable(bool enabled) { + eeprom_write_byte((uint8_t*)eeprom::INVERTED_EXTRUDER_5D,(enabled)?1:0); } -void OverrideGCodeTempMenu::handleSelect(uint8_t index) { - switch (index) { - case 2: - eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,0); - interface::popScreen(); - break; - case 3: - eeprom_write_byte((uint8_t*)eeprom::OVERRIDE_GCODE_TEMP,1); - interface::popScreen(); - break; - } +void Extruder5DMenu::setupTitle() { + msg1 = "Inverted Extdr"; + msg2 = "(5D only):"; } StepperDriverAcceleratedMenu::StepperDriverAcceleratedMenu() { - itemCount = 5; + itemCount = 6; reset(); } void StepperDriverAcceleratedMenu::resetState() { - uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); - if ( accel == 0x03 ) itemIndex = 4; - else if ( accel == 0x01 ) itemIndex = 3; + uint8_t accel = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, EEPROM_DEFAULT_STEPPER_DRIVER); + if ( accel == 0x03 ) itemIndex = 5; + else if ( accel == 0x01 ) itemIndex = 4; + else if ( accel == 0x04 ) itemIndex = 3; else itemIndex = 2; firstItemIndex = 2; } void StepperDriverAcceleratedMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { - const static PROGMEM prog_uchar msg1[] = "Accelerated"; - const static PROGMEM prog_uchar msg2[] = "Stepper Driver:"; - const static PROGMEM prog_uchar off[] = "Off"; - const static PROGMEM prog_uchar on[] = "On - No Planner"; - const static PROGMEM prog_uchar planner[]= "On - Planner"; + const static PROGMEM prog_uchar msg1[] = "Accelerated"; + const static PROGMEM prog_uchar msg2[] = "Stepper Driver:"; + const static PROGMEM prog_uchar off[] = "Off"; + const static PROGMEM prog_uchar offStrangled[] = "Off - Strangled"; + const static PROGMEM prog_uchar on[] = "On - No Planner"; + const static PROGMEM prog_uchar planner[] = "On - Planner"; switch (index) { case 0: @@ -3785,16 +3716,19 @@ void StepperDriverAcceleratedMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { lcd.writeFromPgmspace(off); break; case 3: - lcd.writeFromPgmspace(on); + lcd.writeFromPgmspace(offStrangled); break; case 4: + lcd.writeFromPgmspace(on); + break; + case 5: lcd.writeFromPgmspace(planner); break; } } void StepperDriverAcceleratedMenu::handleSelect(uint8_t index) { - uint8_t oldValue = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0); + uint8_t oldValue = eeprom::getEeprom8(eeprom::STEPPER_DRIVER, EEPROM_DEFAULT_STEPPER_DRIVER); uint8_t newValue = oldValue; switch (index) { @@ -3803,10 +3737,14 @@ void StepperDriverAcceleratedMenu::handleSelect(uint8_t index) { interface::popScreen(); break; case 3: + newValue = 0x04; + interface::popScreen(); + break; + case 4: newValue = 0x01; interface::popScreen(); break; - case 4: + case 5: newValue = 0x03; interface::popScreen(); break; @@ -3827,7 +3765,7 @@ void StepperDriverAcceleratedMenu::handleSelect(uint8_t index) { void writeProfileToEeprom(uint8_t pIndex, uint8_t *pName, int32_t homeX, int32_t homeY, int32_t homeZ, uint8_t hbpTemp, - uint8_t tool0Temp, uint8_t tool1Temp, uint8_t extruderRpm) { + uint8_t tool0Temp, uint8_t tool1Temp, uint8_t extruderMMS) { uint16_t offset = eeprom::PROFILE_BASE + (uint16_t)pIndex * PROFILE_NEXT_OFFSET; cli(); @@ -3841,18 +3779,18 @@ void writeProfileToEeprom(uint8_t pIndex, uint8_t *pName, int32_t homeX, eeprom_write_block(&homeY, (void*) offset, 4); offset += 4; eeprom_write_block(&homeZ, (void*) offset, 4); offset += 4; - //Write temps and extruder RPM + //Write temps and extruder MMS eeprom_write_byte((uint8_t *)offset, hbpTemp); offset += 1; eeprom_write_byte((uint8_t *)offset, tool0Temp); offset += 1; eeprom_write_byte((uint8_t *)offset, tool1Temp); offset += 1; - eeprom_write_byte((uint8_t *)offset, extruderRpm); offset += 1; + eeprom_write_byte((uint8_t *)offset, extruderMMS); offset += 1; sei(); } void readProfileFromEeprom(uint8_t pIndex, uint8_t *pName, int32_t *homeX, int32_t *homeY, int32_t *homeZ, uint8_t *hbpTemp, - uint8_t *tool0Temp, uint8_t *tool1Temp, uint8_t *extruderRpm) { + uint8_t *tool0Temp, uint8_t *tool1Temp, uint8_t *extruderMMS) { uint16_t offset = eeprom::PROFILE_BASE + (uint16_t)pIndex * PROFILE_NEXT_OFFSET; cli(); @@ -3866,11 +3804,11 @@ void readProfileFromEeprom(uint8_t pIndex, uint8_t *pName, int32_t *homeX, eeprom_read_block(homeY, (void*) offset, 4); offset += 4; eeprom_read_block(homeZ, (void*) offset, 4); offset += 4; - //Write temps and extruder RPM + //Write temps and extruder MMS *hbpTemp = eeprom_read_byte((uint8_t *)offset); offset += 1; *tool0Temp = eeprom_read_byte((uint8_t *)offset); offset += 1; *tool1Temp = eeprom_read_byte((uint8_t *)offset); offset += 1; - *extruderRpm = eeprom_read_byte((uint8_t *)offset); offset += 1; + *extruderMMS = eeprom_read_byte((uint8_t *)offset); offset += 1; sei(); } @@ -3907,7 +3845,7 @@ ProfilesMenu::ProfilesMenu() { //Setup defaults if required //If the value is 0xff, write the profile number - uint8_t buf[PROFILE_NAME_LENGTH]; + uint8_t buf[PROFILE_NAME_LENGTH+1]; const static PROGMEM prog_uchar defaultProfile[] = "Profile?"; @@ -3986,14 +3924,14 @@ void ProfileSubMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { } void ProfileSubMenu::handleSelect(uint8_t index) { - uint8_t hbpTemp, tool0Temp, tool1Temp, extruderRpm; + uint8_t hbpTemp, tool0Temp, tool1Temp, extruderMMS; switch (index) { case 0: //Restore //Read settings from eeprom readProfileFromEeprom(profileIndex, NULL, &homePosition[0], &homePosition[1], &homePosition[2], - &hbpTemp, &tool0Temp, &tool1Temp, &extruderRpm); + &hbpTemp, &tool0Temp, &tool1Temp, &extruderMMS); //Write out the home offsets for (uint8_t i = 0; i < PROFILES_SAVED_AXIS; i++) { @@ -4007,7 +3945,7 @@ void ProfileSubMenu::handleSelect(uint8_t index) { eeprom_write_byte((uint8_t *)eeprom::PLATFORM_TEMP, hbpTemp); eeprom_write_byte((uint8_t *)eeprom::TOOL0_TEMP, tool0Temp); eeprom_write_byte((uint8_t *)eeprom::TOOL1_TEMP, tool1Temp); - eeprom_write_byte((uint8_t *)eeprom::EXTRUDE_RPM, extruderRpm); + eeprom_write_byte((uint8_t *)eeprom::EXTRUDE_MMS, extruderMMS); sei(); interface::popScreen(); @@ -4036,13 +3974,13 @@ void ProfileSubMenu::handleSelect(uint8_t index) { sei(); } - hbpTemp = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, 110); - tool0Temp = eeprom::getEeprom8(eeprom::TOOL0_TEMP, 220); - tool1Temp = eeprom::getEeprom8(eeprom::TOOL1_TEMP, 220); - extruderRpm = eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19); + hbpTemp = eeprom::getEeprom8(eeprom::PLATFORM_TEMP, EEPROM_DEFAULT_PLATFORM_TEMP); + tool0Temp = eeprom::getEeprom8(eeprom::TOOL0_TEMP, EEPROM_DEFAULT_TOOL0_TEMP); + tool1Temp = eeprom::getEeprom8(eeprom::TOOL1_TEMP, EEPROM_DEFAULT_TOOL1_TEMP); + extruderMMS = eeprom::getEeprom8(eeprom::EXTRUDE_MMS, EEPROM_DEFAULT_EXTRUDE_MMS); writeProfileToEeprom(profileIndex, NULL, homePosition[0], homePosition[1], homePosition[2], - hbpTemp, tool0Temp, tool1Temp, extruderRpm); + hbpTemp, tool0Temp, tool1Temp, extruderMMS); interface::popScreen(); break; @@ -4138,7 +4076,7 @@ ProfileDisplaySettingsMenu::ProfileDisplaySettingsMenu() { void ProfileDisplaySettingsMenu::resetState() { readProfileFromEeprom(profileIndex, profileName, &homeX, &homeY, &homeZ, - &hbpTemp, &tool0Temp, &tool1Temp, &extruderRpm); + &hbpTemp, &tool0Temp, &tool1Temp, &extruderMMS); itemIndex = 2; firstItemIndex = 2; } @@ -4149,7 +4087,7 @@ void ProfileDisplaySettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { const static PROGMEM prog_uchar zOffset[] = "ZOff: "; const static PROGMEM prog_uchar hbp[] = "HBP Temp: "; const static PROGMEM prog_uchar tool0[] = "Tool0 Temp: "; - const static PROGMEM prog_uchar extruder[] = "ExtrdrRPM: "; + const static PROGMEM prog_uchar extruder[] = "ExtrdrMM/s: "; switch (index) { case 0: @@ -4177,7 +4115,7 @@ void ProfileDisplaySettingsMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { break; case 7: lcd.writeFromPgmspace(extruder); - lcd.writeFloat((float)extruderRpm / 10.0, 1); + lcd.writeFloat((float)extruderMMS, 0); break; } } @@ -4273,22 +4211,32 @@ void UnableToOpenFileMenu::handleSelect(uint8_t index) { void AcceleratedSettingsMode::reset() { cli(); - values[0] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, 160); - values[1] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, 160); - values[2] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, 10); - values[3] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, 100); - values[4] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, 2000); - values[5] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, 2000); - values[6] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, 150); - values[7] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, 60000); - values[8] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, 5000); - values[9] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, 3000); - values[10] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE,0); - values[11] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE,0); - values[12] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_XY_JERK,2); - values[13] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_Z_JERK,100); - values[14] = eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K,50); - values[15] = eeprom::getEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER,175); + values[0] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X); + values[1] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Y, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y); + values[2] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_Z, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Z); + values[3] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_A, EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_A); + values[4] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_X, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_X); + values[5] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Y, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Y); + values[6] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_Z, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_Z); + values[7] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_ACCELERATION_A, EEPROM_DEFAULT_ACCEL_MAX_ACCELERATION_A); + values[8] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_NORM, EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_NORM); + values[9] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, EEPROM_DEFAULT_ACCEL_MAX_EXTRUDER_RETRACT); + values[10] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE, EEPROM_DEFAULT_ACCEL_MIN_FEED_RATE); + values[11] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE, EEPROM_DEFAULT_ACCEL_MIN_TRAVEL_FEED_RATE); + values[12] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_PLANNER_SPEED, EEPROM_DEFAULT_ACCEL_MIN_PLANNER_SPEED); + values[13] = eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K, EEPROM_DEFAULT_ACCEL_ADVANCE_K); + values[14] = eeprom::getEepromUInt32(eeprom::ACCEL_ADVANCE_K2, EEPROM_DEFAULT_ACCEL_ADVANCE_K2); + values[15] = eeprom::getEepromUInt32(eeprom::ACCEL_NOODLE_DIAMETER, EEPROM_DEFAULT_ACCEL_NOODLE_DIAMETER); + values[16] = eeprom::getEepromUInt32(eeprom::ACCEL_MIN_SEGMENT_TIME, EEPROM_DEFAULT_ACCEL_MIN_SEGMENT_TIME); + values[17] = eeprom::getEepromUInt32(eeprom::ACCEL_REV_MAX_FEED_RATE, EEPROM_DEFAULT_ACCEL_REV_MAX_FEED_RATE); + values[18] = eeprom::getEepromUInt32(eeprom::ACCEL_EXTRUDER_DEPRIME, EEPROM_DEFAULT_ACCEL_EXTRUDER_DEPRIME); + values[19] = eeprom::getEepromUInt32(eeprom::ACCEL_SLOWDOWN_LIMIT, EEPROM_DEFAULT_ACCEL_SLOWDOWN_LIMIT); + values[20] = eeprom::getEepromUInt32(eeprom::ACCEL_CLOCKWISE_EXTRUDER, EEPROM_DEFAULT_ACCEL_CLOCKWISE_EXTRUDER); + values[21] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_X, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_X); + values[22] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Y, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Y); + values[23] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Z, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_Z); + values[24] = eeprom::getEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_A, EEPROM_DEFAULT_ACCEL_MAX_SPEED_CHANGE_A); + sei(); lastAccelerateSettingsState= AS_NONE; @@ -4308,10 +4256,19 @@ void AcceleratedSettingsMode::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar message1ExtruderRetract[] = "Acc Extr Move:"; const static PROGMEM prog_uchar message1MinFeedRate[] = "Min Feed Rate:"; const static PROGMEM prog_uchar message1MinTravelFeedRate[] = "MinTrvlFeedRate:"; - const static PROGMEM prog_uchar message1MaxXYJerk[] = "Max XY Jerk:"; - const static PROGMEM prog_uchar message1MaxZJerk[] = "Max Z Jerk:"; - const static PROGMEM prog_uchar message1AdvanceK[] = "Advance K:"; - const static PROGMEM prog_uchar message1FilamentDiameter[] = "Filament Dia:"; + const static PROGMEM prog_uchar message1MinPlannerSpeed[] = "MinPlannerSpeed:"; + const static PROGMEM prog_uchar message1AdvanceK[] = "JKN Advance K:"; + const static PROGMEM prog_uchar message1AdvanceK2[] = "JKN Advance K2:"; + const static PROGMEM prog_uchar message1NoodleDiameter[] = "Noodle Diameter:"; + const static PROGMEM prog_uchar message1MinSegmentTime[] = "Min Seg Time:"; + const static PROGMEM prog_uchar message1RevMaxFeedRate[] = "RevMaxFeedRate:"; + const static PROGMEM prog_uchar message1ExtruderDeprime[] = "ExtruderDeprime:"; + const static PROGMEM prog_uchar message1SlowdownLimit[] = "Slowdown Limit:"; + const static PROGMEM prog_uchar message1ClockwiseExtruder[] = "Clockwise Extdr:"; + const static PROGMEM prog_uchar message1MaxSpeedChangeX[] = "MaxSpeedChangeX:"; + const static PROGMEM prog_uchar message1MaxSpeedChangeY[] = "MaxSpeedChangeY:"; + const static PROGMEM prog_uchar message1MaxSpeedChangeZ[] = "MaxSpeedChangeZ:"; + const static PROGMEM prog_uchar message1MaxSpeedChangeA[] = "MaxSpeedChangeA:"; const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; const static PROGMEM prog_uchar blank[] = " "; @@ -4358,17 +4315,44 @@ void AcceleratedSettingsMode::update(LiquidCrystal& lcd, bool forceRedraw) { case AS_MIN_TRAVEL_FEED_RATE: lcd.writeFromPgmspace(message1MinTravelFeedRate); break; - case AS_MAX_XY_JERK: - lcd.writeFromPgmspace(message1MaxXYJerk); - break; - case AS_MAX_Z_JERK: - lcd.writeFromPgmspace(message1MaxZJerk); + case AS_MIN_PLANNER_SPEED: + lcd.writeFromPgmspace(message1MinPlannerSpeed); break; case AS_ADVANCE_K: lcd.writeFromPgmspace(message1AdvanceK); break; - case AS_FILAMENT_DIAMETER: - lcd.writeFromPgmspace(message1FilamentDiameter); + case AS_ADVANCE_K2: + lcd.writeFromPgmspace(message1AdvanceK2); + break; + case AS_NOODLE_DIAMETER: + lcd.writeFromPgmspace(message1NoodleDiameter); + break; + case AS_MIN_SEGMENT_TIME: + lcd.writeFromPgmspace(message1MinSegmentTime); + break; + case AS_REV_MAX_FEED_RATE: + lcd.writeFromPgmspace(message1RevMaxFeedRate); + break; + case AS_EXTRUDER_DEPRIME: + lcd.writeFromPgmspace(message1ExtruderDeprime); + break; + case AS_SLOWDOWN_LIMIT: + lcd.writeFromPgmspace(message1SlowdownLimit); + break; + case AS_CLOCKWISE_EXTRUDER: + lcd.writeFromPgmspace(message1ClockwiseExtruder); + break; + case AS_MAX_SPEED_CHANGE_X: + lcd.writeFromPgmspace(message1MaxSpeedChangeX); + break; + case AS_MAX_SPEED_CHANGE_Y: + lcd.writeFromPgmspace(message1MaxSpeedChangeY); + break; + case AS_MAX_SPEED_CHANGE_Z: + lcd.writeFromPgmspace(message1MaxSpeedChangeZ); + break; + case AS_MAX_SPEED_CHANGE_A: + lcd.writeFromPgmspace(message1MaxSpeedChangeA); break; } @@ -4387,28 +4371,34 @@ void AcceleratedSettingsMode::update(LiquidCrystal& lcd, bool forceRedraw) { switch(accelerateSettingsState) { case AS_MIN_FEED_RATE: case AS_MIN_TRAVEL_FEED_RATE: - case AS_MAX_XY_JERK: - case AS_MAX_Z_JERK: + case AS_MAX_SPEED_CHANGE_X: + case AS_MAX_SPEED_CHANGE_Y: + case AS_MAX_SPEED_CHANGE_Z: + case AS_MAX_SPEED_CHANGE_A: + case AS_EXTRUDER_DEPRIME: lcd.writeFloat((float)value / 10.0, 1); break; case AS_ADVANCE_K: + case AS_ADVANCE_K2: lcd.writeFloat((float)value / 100000.0, 5); break; - case AS_FILAMENT_DIAMETER: + case AS_NOODLE_DIAMETER: lcd.writeFloat((float)value / 100.0, 2); break; + case AS_MIN_SEGMENT_TIME: + lcd.writeFloat((float)value / 10000.0, 4); + break; default: lcd.writeFloat((float)value, 0); break; } - lcd.writeFromPgmspace(blank); lastAccelerateSettingsState = accelerateSettingsState; } void AcceleratedSettingsMode::notifyButtonPressed(ButtonArray::ButtonName button) { - if (( accelerateSettingsState == AS_FILAMENT_DIAMETER ) && (button == ButtonArray::OK )) { + if (( accelerateSettingsState == AS_LAST_ENTRY ) && (button == ButtonArray::OK )) { //Write the data cli(); eeprom::putEepromUInt32(eeprom::ACCEL_MAX_FEEDRATE_X, values[0]); @@ -4423,10 +4413,19 @@ void AcceleratedSettingsMode::notifyButtonPressed(ButtonArray::ButtonName button eeprom::putEepromUInt32(eeprom::ACCEL_MAX_EXTRUDER_RETRACT, values[9]); eeprom::putEepromUInt32(eeprom::ACCEL_MIN_FEED_RATE, values[10]); eeprom::putEepromUInt32(eeprom::ACCEL_MIN_TRAVEL_FEED_RATE, values[11]); - eeprom::putEepromUInt32(eeprom::ACCEL_MAX_XY_JERK, values[12]); - eeprom::putEepromUInt32(eeprom::ACCEL_MAX_Z_JERK, values[13]); - eeprom::putEepromUInt32(eeprom::ACCEL_ADVANCE_K, values[14]); - eeprom::putEepromUInt32(eeprom::ACCEL_FILAMENT_DIAMETER, values[15]); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_PLANNER_SPEED, values[12]); + eeprom::putEepromUInt32(eeprom::ACCEL_ADVANCE_K, values[13]); + eeprom::putEepromUInt32(eeprom::ACCEL_ADVANCE_K2, values[14]); + eeprom::putEepromUInt32(eeprom::ACCEL_NOODLE_DIAMETER, values[15]); + eeprom::putEepromUInt32(eeprom::ACCEL_MIN_SEGMENT_TIME, values[16]); + eeprom::putEepromUInt32(eeprom::ACCEL_REV_MAX_FEED_RATE, values[17]); + eeprom::putEepromUInt32(eeprom::ACCEL_EXTRUDER_DEPRIME, values[18]); + eeprom::putEepromUInt32(eeprom::ACCEL_SLOWDOWN_LIMIT, values[19]); + eeprom::putEepromUInt32(eeprom::ACCEL_CLOCKWISE_EXTRUDER, values[20]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_X, values[21]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Y, values[22]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_Z, values[23]); + eeprom::putEepromUInt32(eeprom::ACCEL_MAX_SPEED_CHANGE_A, values[24]); sei(); host::stopBuild(); @@ -4468,11 +4467,22 @@ void AcceleratedSettingsMode::notifyButtonPressed(ButtonArray::ButtonName button break; } - if (!(( accelerateSettingsState == AS_MIN_FEED_RATE ) || ( accelerateSettingsState == AS_MIN_TRAVEL_FEED_RATE ) || ( accelerateSettingsState == AS_ADVANCE_K ))) { + //Settings that allow a zero value + if (!(( accelerateSettingsState == AS_MIN_FEED_RATE ) || ( accelerateSettingsState == AS_MIN_TRAVEL_FEED_RATE ) || + ( accelerateSettingsState == AS_ADVANCE_K ) || ( accelerateSettingsState == AS_ADVANCE_K2 ) || + ( accelerateSettingsState == AS_MIN_SEGMENT_TIME) || ( accelerateSettingsState == AS_EXTRUDER_DEPRIME ) || + ( accelerateSettingsState == AS_SLOWDOWN_LIMIT ) || ( accelerateSettingsState == AS_CLOCKWISE_EXTRUDER ))) { if ( values[currentIndex] < 1 ) values[currentIndex] = 1; } if ( values[currentIndex] > 200000 ) values[currentIndex] = 1; + + //Settings that have a maximum value + if (( accelerateSettingsState == AS_SLOWDOWN_LIMIT ) && ( values[currentIndex] > (BLOCK_BUFFER_SIZE >> 1))) + values[currentIndex] = (BLOCK_BUFFER_SIZE >> 1); + + if (( accelerateSettingsState == AS_CLOCKWISE_EXTRUDER ) && ( values[currentIndex] > 1)) + values[currentIndex] = 1; } AccelerationMenu::AccelerationMenu() { @@ -4482,8 +4492,8 @@ AccelerationMenu::AccelerationMenu() { } void AccelerationMenu::resetState() { - if ( eeprom::getEeprom8(eeprom::STEPPER_DRIVER, 0) ) acceleration = true; - else acceleration = false; + if ( eeprom::getEeprom8(eeprom::STEPPER_DRIVER, EEPROM_DEFAULT_STEPPER_DRIVER) & 0x01 ) acceleration = true; + else acceleration = false; if ( acceleration ) itemCount = 3; else itemCount = 1; @@ -4529,10 +4539,10 @@ void AccelerationMenu::handleSelect(uint8_t index) { } void EStepsPerMMMode::reset() { - value = eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, 44); + value = eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM); if ( value < 1 ) { - eeprom::putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, 44); - value = eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, 44); //Just in case + eeprom::putEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM); + value = eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM); //Just in case } } @@ -4603,7 +4613,7 @@ void EStepsPerMMMode::notifyButtonPressed(ButtonArray::ButtonName button) { void EStepsPerMMStepsMode::reset() { value = 200; - steppers::switchToRegularDriver(); + steppers::switchToRegularDriver(true); overrideExtrudeSeconds = 0; } @@ -4653,13 +4663,16 @@ void EStepsPerMMStepsMode::extrude(bool overrideTempCheck) { Point position = steppers::getPosition(); - float rpm = (float)eeprom::getEeprom8(eeprom::EXTRUDE_RPM, 19) / 10.0; + float mms = (float)eeprom::getEeprom8(eeprom::EXTRUDE_MMS, EEPROM_DEFAULT_EXTRUDE_MMS); + float eStepsPerMM = (float)eeprom::getEepromUInt32(eeprom::ACCEL_E_STEPS_PER_MM, EEPROM_DEFAULT_ACCEL_E_STEPS_PER_MM) / 10.0; + float stepsPerSecond = mms * eStepsPerMM; + int32_t interval = (int32_t)(1000000.0 / stepsPerSecond); - //60 * 1000000 = # uS in a minute - //200 * 8 = 200 steps per revolution * 1/8 stepping - int32_t interval = (int32_t)(60L * 1000000L) / (int32_t)((float)(200 * 8) * rpm); + //Handle 5D + int32_t direction5d = 1; + if ( eeprom::getEeprom8(eeprom::INVERTED_EXTRUDER_5D, EEPROM_DEFAULT_INVERTED_EXTRUDER_5D) == 1 ) direction5d = -1; - position[3] += -value; + position[3] += (-value) * direction5d; steppers::setTarget(position, interval); if (overrideTempCheck) overrideExtrudeSeconds = 0; @@ -4776,4 +4789,243 @@ void EStepsPerMMLengthMode::notifyButtonPressed(ButtonArray::ButtonName button) if (( value < 1 ) || ( value > 200000 )) value = 1; } +void EndStopConfigScreen::reset() { + endstops = eeprom::getEeprom8(eeprom::ENDSTOPS_USED, EEPROM_DEFAULT_ENDSTOPS_USED); +} + +void EndStopConfigScreen::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1[] = "EndstopsPresent:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + lcd.writeFromPgmspace(message1); + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + // Redraw tool info + lcd.setCursor(0,1); + lcd.writeFloat((float)endstops, 0); + lcd.writeFromPgmspace(blank); +} + +void EndStopConfigScreen::notifyButtonPressed(ButtonArray::ButtonName button) { + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + eeprom_write_byte((uint8_t *)eeprom::ENDSTOPS_USED, endstops); + interface::popScreen(); + break; + case ButtonArray::ZPLUS: + // increment more + if (endstops <= 122) endstops += 5; + break; + case ButtonArray::ZMINUS: + // decrement more + if (endstops >= 5) endstops -= 5; + break; + case ButtonArray::YPLUS: + // increment less + if (endstops <= 126) endstops += 1; + break; + case ButtonArray::YMINUS: + // decrement less + if (endstops >= 1) endstops -= 1; + break; + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } +} + +void HomingFeedRatesMode::reset() { + cli(); + homingFeedRate[0] = eeprom::getEepromUInt32(eeprom::HOMING_FEED_RATE_X, EEPROM_DEFAULT_HOMING_FEED_RATE_X); + homingFeedRate[1] = eeprom::getEepromUInt32(eeprom::HOMING_FEED_RATE_Y, EEPROM_DEFAULT_HOMING_FEED_RATE_Y); + homingFeedRate[2] = eeprom::getEepromUInt32(eeprom::HOMING_FEED_RATE_Z, EEPROM_DEFAULT_HOMING_FEED_RATE_Z); + sei(); + + lastHomingFeedRateState = HFRS_NONE; + homingFeedRateState = HFRS_OFFSET_X; +} + +void HomingFeedRatesMode::update(LiquidCrystal& lcd, bool forceRedraw) { + const static PROGMEM prog_uchar message1x[] = "X Home Feedrate:"; + const static PROGMEM prog_uchar message1y[] = "Y Home Feedrate:"; + const static PROGMEM prog_uchar message1z[] = "Z Home Feedrate:"; + const static PROGMEM prog_uchar message4[] = "Up/Dn/Ent to Set"; + const static PROGMEM prog_uchar blank[] = " "; + const static PROGMEM prog_uchar mm[] = "mm/min "; + + if ( homingFeedRateState != lastHomingFeedRateState ) forceRedraw = true; + + if (forceRedraw) { + lcd.clear(); + + lcd.setCursor(0,0); + switch(homingFeedRateState) { + case HFRS_OFFSET_X: + lcd.writeFromPgmspace(message1x); + break; + case HFRS_OFFSET_Y: + lcd.writeFromPgmspace(message1y); + break; + case HFRS_OFFSET_Z: + lcd.writeFromPgmspace(message1z); + break; + } + + lcd.setCursor(0,3); + lcd.writeFromPgmspace(message4); + } + + float feedRate = 0.0; + + switch(homingFeedRateState) { + case HFRS_OFFSET_X: + feedRate = homingFeedRate[0]; + break; + case HFRS_OFFSET_Y: + feedRate = homingFeedRate[1]; + break; + case HFRS_OFFSET_Z: + feedRate = homingFeedRate[2]; + break; + } + + lcd.setCursor(0,1); + lcd.writeFloat((float)feedRate, 0); + lcd.writeFromPgmspace(mm); + + lastHomingFeedRateState = homingFeedRateState; +} + +void HomingFeedRatesMode::notifyButtonPressed(ButtonArray::ButtonName button) { + if (( homingFeedRateState == HFRS_OFFSET_Z ) && (button == ButtonArray::OK )) { + //Write the new homing feed rates + cli(); + eeprom::putEepromUInt32(eeprom::HOMING_FEED_RATE_X, homingFeedRate[0]); + eeprom::putEepromUInt32(eeprom::HOMING_FEED_RATE_Y, homingFeedRate[1]); + eeprom::putEepromUInt32(eeprom::HOMING_FEED_RATE_Z, homingFeedRate[2]); + sei(); + + interface::popScreen(); + } + + uint8_t currentIndex = homingFeedRateState - HFRS_OFFSET_X; + + switch (button) { + case ButtonArray::CANCEL: + interface::popScreen(); + break; + case ButtonArray::ZERO: + break; + case ButtonArray::OK: + if ( homingFeedRateState == HFRS_OFFSET_X ) homingFeedRateState = HFRS_OFFSET_Y; + else if ( homingFeedRateState == HFRS_OFFSET_Y ) homingFeedRateState = HFRS_OFFSET_Z; + break; + case ButtonArray::ZPLUS: + // increment more + homingFeedRate[currentIndex] += 20; + break; + case ButtonArray::ZMINUS: + // decrement more + if ( homingFeedRate[currentIndex] >= 21 ) + homingFeedRate[currentIndex] -= 20; + break; + case ButtonArray::YPLUS: + // increment less + homingFeedRate[currentIndex] += 1; + break; + case ButtonArray::YMINUS: + // decrement less + if ( homingFeedRate[currentIndex] >= 2 ) + homingFeedRate[currentIndex] -= 1; + break; + case ButtonArray::XMINUS: + case ButtonArray::XPLUS: + break; + } + + if (( homingFeedRate[currentIndex] < 1 ) || ( homingFeedRate[currentIndex] > 2000 )) + homingFeedRate[currentIndex] = 1; +} + +#ifdef EEPROM_MENU_ENABLE + +EepromMenu::EepromMenu() { + itemCount = 3; + reset(); +} + +void EepromMenu::resetState() { + itemIndex = 0; + firstItemIndex = 0; + safetyGuard = 0; +} + +void EepromMenu::drawItem(uint8_t index, LiquidCrystal& lcd) { + const static PROGMEM prog_uchar message_dump[] = "Eeprom -> SD"; + const static PROGMEM prog_uchar message_restore[] = "SD -> Eeprom"; + const static PROGMEM prog_uchar message_erase[] = "Erase Eeprom"; + switch (index) + { + case 0: + lcd.writeFromPgmspace(message_dump); + break; + case 1: + lcd.writeFromPgmspace(message_restore); + break; + case 2: + lcd.writeFromPgmspace(message_erase); + break; + } +} + +void EepromMenu::handleSelect(uint8_t index) { + const char dumpFilename[] = "eeprom_dump.bin"; + + switch (index) + { + case 0: + //Dump + safetyGuard = 0; + Motherboard::getBoard().buzz(2, 2, 1); + eeprom::saveToSDFile(dumpFilename); + interface::popScreen(); + break; + case 1: + //Restore + safetyGuard ++; + if ( safetyGuard > 3 ) { + Motherboard::getBoard().buzz(2, 2, 1); + safetyGuard = 0; + eeprom::restoreFromSDFile(dumpFilename); + interface::popScreen(); + } + break; + case 2: + //Erase + safetyGuard ++; + if ( safetyGuard > 3 ) { + safetyGuard = 0; + Motherboard::getBoard().buzz(2, 2, 1); + eeprom::erase(); + interface::popScreen(); + } + break; + } +} + +#endif + #endif diff --git a/firmware/src/shared/Menu.hh b/firmware/src/shared/Menu.hh index 41d7ec8..3871713 100644 --- a/firmware/src/shared/Menu.hh +++ b/firmware/src/shared/Menu.hh @@ -4,6 +4,7 @@ #include "Types.hh" #include "ButtonArray.hh" #include "LiquidCrystal.hh" +#include "Configuration.hh" /// The screen class defines a standard interface for anything that should /// be displayed on the LCD. @@ -100,75 +101,37 @@ protected: void handleSelect(uint8_t index); }; -class JogMode: public Screen { -private: - enum distance_t { - DISTANCE_0_1MM = 0, - DISTANCE_1MM, - DISTANCE_CONT, - }; +class JoggerMenu: public Screen { +public: + enum distance_t { + DISTANCE_0_1MM = 0, + DISTANCE_1MM, + DISTANCE_CONT, + }; - UserViewMenu userViewMenu; + micros_t getUpdateRate() {return 50L * 1000L;} distance_t jogDistance; - bool distanceChanged; - bool userViewMode; - bool userViewModeChanged; - ButtonArray::ButtonName lastDirectionButtonPressed; - void jog(ButtonArray::ButtonName direction); - -public: - micros_t getUpdateRate() {return 50L * 1000L;} - - void update(LiquidCrystal& lcd, bool forceRedraw); + ButtonArray::ButtonName lastDirectionButtonPressed; - void reset(); + void jog(ButtonArray::ButtonName direction, bool pauseModeJog); - void notifyButtonPressed(ButtonArray::ButtonName button); + bool userViewMode; }; -/// This is an easter egg. -class SnakeMode: public Screen { - -#define MAX_SNAKE_SIZE 20 ///< Maximum length our snake can grow to -#define APPLES_BEFORE_GROW 4 ///< Number of apples the snake must eat before growing -#define START_SPEED 60 ///< Starting speed, in screen refresh times per turn - - +class JogMode: public JoggerMenu { private: - micros_t updateRate; - - struct coord_t { - int8_t x; - int8_t y; - }; - - enum direction_t { - DIR_NORTH, - DIR_EAST, - DIR_SOUTH, - DIR_WEST - }; - - int snakeLength; // Length of our snake; this grows for every x 'apples' eaten - coord_t snakeBody[MAX_SNAKE_SIZE]; // Table of each piece of the snakes body - bool snakeAlive; // The state of our snake - direction_t snakeDirection; // The direction the snake is heading - coord_t applePosition; // Location of the apple - uint8_t applesEaten; // Number of apples that have been eaten -// int gameSpeed = START_SPEED; // Speed of the game (in ms per turn) + UserViewMenu userViewMenu; + bool distanceChanged; + bool userViewModeChanged; public: - micros_t getUpdateRate() {return updateRate;} - - // Refresh the display information void update(LiquidCrystal& lcd, bool forceRedraw); void reset(); - // Get notified that a button was pressed void notifyButtonPressed(ButtonArray::ButtonName button); }; @@ -210,19 +173,15 @@ protected: void handleSelect(uint8_t index); }; - -class PauseMode: public Screen { +class PauseMode: public JoggerMenu { private: - ButtonArray::ButtonName lastDirectionButtonPressed; - - void jog(ButtonArray::ButtonName direction); - uint8_t pauseState; public: bool autoPause; - - micros_t getUpdateRate() {return 50L * 1000L;} + bool noheatPause; + uint16_t tool0SetPoint; + uint16_t hbpSetPoint; void update(LiquidCrystal& lcd, bool forceRedraw); @@ -286,6 +245,7 @@ private: BUILD_TIME_PHASE_ZPOS, BUILD_TIME_PHASE_FILAMENT, BUILD_TIME_PHASE_COPIES_PRINTED, + BUILD_TIME_PHASE_ACCEL_STATS, BUILD_TIME_PHASE_LAST //Not counted, just an end marker }; @@ -299,6 +259,9 @@ private: bool overrideForceRedraw; uint8_t copiesPrinted; bool timeLeftDisplayed; + bool toggleHeating; + bool flashingTool; + bool flashingPlatform; public: micros_t getUpdateRate() {return 500L * 1000L;} @@ -325,26 +288,16 @@ public: }; -class Tool0TempSetScreen: public Screen { +class ValueSetScreen: public Screen { private: uint8_t value; public: - micros_t getUpdateRate() {return 100L * 1000L;} - - void update(LiquidCrystal& lcd, bool forceRedraw); - - void reset(); - - void notifyButtonPressed(ButtonArray::ButtonName button); -}; + char *message1; + char *units; + uint16_t location; + uint8_t default_value; - -class PlatformTempSetScreen: public Screen { -private: - uint8_t value; - -public: micros_t getUpdateRate() {return 100L * 1000L;} void update(LiquidCrystal& lcd, bool forceRedraw); @@ -371,8 +324,7 @@ private: uint16_t platformTemp; /// Static instances of our menus - Tool0TempSetScreen tool0TempSetScreen; - PlatformTempSetScreen platTempSetScreen; + ValueSetScreen heaterTempSetScreen; }; class ExtruderTooColdMenu: public Menu { @@ -384,20 +336,8 @@ protected: void drawItem(uint8_t index, LiquidCrystal& lcd); void handleSelect(uint8_t index); -}; - -class ExtruderSetRpmScreen: public Screen { -private: - uint8_t rpm; -public: - micros_t getUpdateRate() {return 100L * 1000L;} - - void update(LiquidCrystal& lcd, bool forceRedraw); - - void reset(); - - void notifyButtonPressed(ButtonArray::ButtonName button); + void handleCancel(); }; class ExtruderMode: public Screen { @@ -419,11 +359,11 @@ private: bool timeChanged; int16_t lastDirection; ExtruderTooColdMenu extruderTooColdMenu; - ExtruderSetRpmScreen extruderSetRpmScreen; + ValueSetScreen extruderSetMMSScreen; uint8_t updatePhase; - void extrude(seconds_t steps, bool overrideTempCheck); + void extrude(int32_t seconds, bool overrideTempCheck); public: micros_t getUpdateRate() {return 50L * 1000L;} @@ -473,9 +413,24 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class EndStopConfigScreen: public Screen { +private: + uint8_t endstops; + +public: + micros_t getUpdateRate() {return 100L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + class HomeAxisMode: public Screen { private: void home(ButtonArray::ButtonName direction); + EndStopConfigScreen endStopConfigScreen; public: micros_t getUpdateRate() {return 50L * 1000L;} @@ -487,15 +442,33 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; -class SteppersMenu: public Menu { +class EnabledDisabledMenu: public Menu { public: - SteppersMenu(); + EnabledDisabledMenu(); void resetState(); + protected: void drawItem(uint8_t index, LiquidCrystal& lcd); void handleSelect(uint8_t index); + + virtual void enable(bool enabled) {}; + + virtual bool isEnabled() {return false;}; + + virtual void setupTitle() {}; + + char *msg1, *msg2; +}; + +class SteppersMenu: public EnabledDisabledMenu { +private: + void enable(bool enabled); + + bool isEnabled(); + + void setupTitle(); }; class TestEndStopsMode: public Screen { @@ -588,15 +561,13 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; -class ExtruderFanMenu: public Menu { -public: - ExtruderFanMenu(); - - void resetState(); -protected: - void drawItem(uint8_t index, LiquidCrystal& lcd); +class ExtruderFanMenu: public EnabledDisabledMenu { +private: + void enable(bool enabled); + + bool isEnabled(); - void handleSelect(uint8_t index); + void setupTitle(); }; class StepsPerMMMode: public Screen { @@ -666,26 +637,22 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; -class PreheatDuringEstimateMenu: public Menu { -public: - PreheatDuringEstimateMenu(); - - void resetState(); -protected: - void drawItem(uint8_t index, LiquidCrystal& lcd); +class PreheatDuringEstimateMenu: public EnabledDisabledMenu { +private: + void enable(bool enabled); + + bool isEnabled(); - void handleSelect(uint8_t index); + void setupTitle(); }; -class OverrideGCodeTempMenu: public Menu { -public: - OverrideGCodeTempMenu(); - - void resetState(); -protected: - void drawItem(uint8_t index, LiquidCrystal& lcd); +class OverrideGCodeTempMenu: public EnabledDisabledMenu { +private: + void enable(bool enabled); + + bool isEnabled(); - void handleSelect(uint8_t index); + void setupTitle(); }; class StepperDriverAcceleratedMenu: public Menu { @@ -699,10 +666,19 @@ protected: void handleSelect(uint8_t index); }; +class Extruder5DMenu: public EnabledDisabledMenu { +private: + void enable(bool enabled); + + bool isEnabled(); + + void setupTitle(); +}; + class AcceleratedSettingsMode: public Screen { private: enum accelerateSettingsState { - AS_NONE, + AS_NONE = 0, AS_MAX_FEEDRATE_X, AS_MAX_FEEDRATE_Y, AS_MAX_FEEDRATE_Z, @@ -715,15 +691,25 @@ private: AS_MAX_EXTRUDER_RETRACT, AS_MIN_FEED_RATE, AS_MIN_TRAVEL_FEED_RATE, - AS_MAX_XY_JERK, - AS_MAX_Z_JERK, + AS_MIN_PLANNER_SPEED, AS_ADVANCE_K, - AS_FILAMENT_DIAMETER, + AS_ADVANCE_K2, + AS_NOODLE_DIAMETER, + AS_MIN_SEGMENT_TIME, + AS_REV_MAX_FEED_RATE, + AS_EXTRUDER_DEPRIME, + AS_SLOWDOWN_LIMIT, + AS_CLOCKWISE_EXTRUDER, + AS_MAX_SPEED_CHANGE_X, + AS_MAX_SPEED_CHANGE_Y, + AS_MAX_SPEED_CHANGE_Z, + AS_MAX_SPEED_CHANGE_A, + AS_LAST_ENTRY = AS_MAX_SPEED_CHANGE_A }; enum accelerateSettingsState accelerateSettingsState, lastAccelerateSettingsState; - uint32_t values[16]; + uint32_t values[AS_LAST_ENTRY]; public: micros_t getUpdateRate() {return 50L * 1000L;} @@ -807,6 +793,7 @@ private: OverrideGCodeTempMenu overrideGCodeTempMenu; ABPCopiesSetScreen abpCopiesSetScreen; AccelerationMenu accelerationMenu; + Extruder5DMenu extruder5DMenu; public: BuildSettingsMenu(); @@ -838,7 +825,7 @@ class ProfileDisplaySettingsMenu: public Menu { private: uint8_t profileName[8+1]; int32_t homeX, homeY, homeZ; - uint8_t hbpTemp, tool0Temp, tool1Temp, extruderRpm; + uint8_t hbpTemp, tool0Temp, tool1Temp, extruderMMS; public: ProfileDisplaySettingsMenu(); @@ -894,6 +881,46 @@ public: void notifyButtonPressed(ButtonArray::ButtonName button); }; +class HomingFeedRatesMode: public Screen { +private: + enum homingFRateState { + HFRS_NONE, + HFRS_OFFSET_X, + HFRS_OFFSET_Y, + HFRS_OFFSET_Z, + }; + + enum homingFRateState homingFeedRateState, lastHomingFeedRateState; + + uint32_t homingFeedRate[3]; + +public: + micros_t getUpdateRate() {return 50L * 1000L;} + + void update(LiquidCrystal& lcd, bool forceRedraw); + + void reset(); + + void notifyButtonPressed(ButtonArray::ButtonName button); +}; + +#ifdef EEPROM_MENU_ENABLE + +class EepromMenu: public Menu { +public: + EepromMenu(); + + void resetState(); +protected: + void drawItem(uint8_t index, LiquidCrystal& lcd); + + void handleSelect(uint8_t index); +private: + uint8_t safetyGuard; +}; + +#endif + class MainMenu: public Menu { public: MainMenu(); @@ -903,6 +930,8 @@ protected: void handleSelect(uint8_t index); + void update(LiquidCrystal& lcd, bool forceRedraw); + private: /// Static instances of our menus MonitorMode monitorMode; @@ -925,9 +954,14 @@ private: TestEndStopsMode testEndStopsMode; VersionMode versionMode; MoodLightMode moodLightMode; - SnakeMode snake; + HomingFeedRatesMode homingFeedRatesMode; +#ifdef EEPROM_MENU_ENABLE + EepromMenu eepromMenu; +#endif int64_t checkAndGetEepromDefault(const uint16_t location, const int64_t default_value); + + float lcdTypeChangeTimer; }; #endif diff --git a/firmware/src/shared/MoodLightController.cc b/firmware/src/shared/MoodLightController.cc index c843c79..0bb51b5 100755 --- a/firmware/src/shared/MoodLightController.cc +++ b/firmware/src/shared/MoodLightController.cc @@ -22,6 +22,7 @@ #include "Configuration.hh" #include "EepromMap.hh" #include "Eeprom.hh" +#include "EepromDefaults.hh" #include @@ -49,7 +50,7 @@ bool MoodLightController::start() { blinkM.stopScript(); #ifdef HAS_MOOD_LIGHT - playScript(eeprom::getEeprom8(eeprom::MOOD_LIGHT_SCRIPT, 0)); + playScript(eeprom::getEeprom8(eeprom::MOOD_LIGHT_SCRIPT, EEPROM_DEFAULT_MOOD_LIGHT_SCRIPT)); #endif return blinkM.blinkMIsPresent; @@ -236,9 +237,9 @@ void MoodLightController::playScript(uint8_t scriptId) { } else if ( scriptId == 1 ) { //Custom RGB (User Defined) #ifdef HAS_MOOD_LIGHT - blinkM.fadeToRGB(eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_RED, 255), - eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_GREEN,255), - eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_BLUE, 255)); + blinkM.fadeToRGB(eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_RED, EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_RED), + eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_GREEN, EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_GREEN), + eeprom::getEeprom8(eeprom::MOOD_LIGHT_CUSTOM_BLUE, EEPROM_DEFAULT_MOOD_LIGHT_CUSTOM_BLUE)); #endif } else if (( scriptId >= 2 ) && ( scriptId < (2 + kDefinedColors ))) { //Regular solid colors uint8_t r, g, b; @@ -262,7 +263,7 @@ void MoodLightController::playScript(uint8_t scriptId) { void MoodLightController::debugLightSetValue(bool value) { #ifdef HAS_MOOD_LIGHT - if ( eeprom::getEeprom8(eeprom::MOOD_LIGHT_SCRIPT, 0) == (uint8_t) 0 ) { + if ( eeprom::getEeprom8(eeprom::MOOD_LIGHT_SCRIPT, EEPROM_DEFAULT_MOOD_LIGHT_SCRIPT) == (uint8_t) 0 ) { eStopTriggered = true; if ( value ) blinkM.setRGB(255, 0, 0); else blinkM.setRGB(0, 0, 0); diff --git a/firmware/src/shared/StepperAccel.cc b/firmware/src/shared/StepperAccel.cc index e051612..ef7e42f 100644 --- a/firmware/src/shared/StepperAccel.cc +++ b/firmware/src/shared/StepperAccel.cc @@ -28,11 +28,17 @@ #include "StepperAccel.hh" + +#ifdef LOOKUP_TABLE_TIMER #include "StepperAccelSpeedTable.hh" +#endif + #include "StepperInterface.hh" #include "Motherboard.hh" -#include +#include +#include +#include @@ -41,7 +47,8 @@ //=============================public variables ============================ //=========================================================================== block_t *current_block; // A pointer to the block currently being traced - +int32_t extruder_deprime_steps; //Positive or negative depending on clockwise_extruder, when it's true, this is positive +bool clockwise_extruder; //True if the extruder extrudes when rotating positive when viewed from the spindle end //=========================================================================== //=============================private variables ============================ @@ -50,28 +57,73 @@ block_t *current_block; // A pointer to the block currently being traced // Variables used by The Stepper Driver Interrupt static unsigned char out_bits; // The next stepping-bits to be output -static int32_t counter_x, // Counter variables for the bresenham line tracer - counter_y, - counter_z, - counter_e; volatile static uint32_t step_events_completed; // The number of step events executed in the current block -#ifdef ADVANCE - static int32_t advance_rate = 0, advance, final_advance = 0; - static int32_t old_advance = 0; +#ifdef JKN_ADVANCE + enum AdvanceState { + ADVANCE_STATE_ACCEL = 0, + ADVANCE_STATE_PLATEAU, + ADVANCE_STATE_DECEL + }; + static enum AdvanceState advance_state; + static int32_t advance_pressure_relax_accumulator; + static int32_t lastAdvanceDeprime; + + #define ADVANCE_INTERRUPT_FREQUENCY 10000 //10KHz + + static uint32_t st_advance_interrupt_rate = 0; + static uint8_t advance_interrupt_steps_per_call = 0; + static uint32_t st_advance_interrupt_rate_counter[EXTRUDERS]; + volatile int32_t starting_e_position = 0; #endif -volatile static int32_t e_steps[3]; -volatile static unsigned char busy = false; // TRUE when SIG_OUTPUT_COMPARE1A is being serviced. Used to avoid retriggering that handler. +volatile static int32_t e_steps[EXTRUDERS]; static int32_t acceleration_time, deceleration_time; //static uint32_t accelerate_until, decelerate_after, acceleration_rate, initial_rate, final_rate, nominal_rate; -static unsigned short acc_step_rate; // needed for deccelaration start point -static char step_loops; -static unsigned short OCR1A_nominal; +static uint16_t acc_step_rate, step_rate; +static char step_loops, step_loops_nominal; +static uint16_t OCR1A_nominal; volatile int32_t count_position[NUM_AXIS] = { 0, 0, 0, 0}; -volatile char count_direction[NUM_AXIS] = { 1, 1, 1, 1}; static StepperInterface *stepperInterface; +static bool deprimed = true; +static unsigned char deprimeIndex = 0; + +//STEP_TRUE, runs stepperInterface[axis].step(true) +//If CHECK_ENDSTOPS_ENABLED is defined, it checks the ends stops haven't been hit before moving +//and doesn't move if they have + +#ifdef CHECK_ENDSTOPS_ENABLED + #define STEP_TRUE(axis, direction) if (( (direction) && (! stepperInterface[axis].isAtMaximum())) || \ + ( (! (direction)) && (! stepperInterface[axis].isAtMinimum()))) \ + stepperInterface[axis].step(true) +#else + #define STEP_TRUE(axis, direction) stepperInterface[axis].step(true) +#endif + +#if defined(DEBUG_TIMER) && defined(TCNT3) +uint16_t debugTimer; +#endif + +#ifdef OVERSAMPLED_DDA + uint8_t oversampledCount = 0; +#endif + +struct dda { + bool master; //True if this is the master steps axis + int32_t master_steps; //The number of steps for the master axis + bool eAxis; //True if this is the e axis + char direction; //Direction of the dda, 1 = forward, -1 = backwards + bool stepperInterfaceDir; //The direction the stepper interface gets sent in + + int32_t counter; //Used for the dda counter + int32_t steps_completed; //Number of steps completed + int32_t steps; //Number of steps we need to execute for this axis +}; + +struct dda ddas[NUM_AXIS]; + + //=========================================================================== //=============================functions ============================ //=========================================================================== @@ -168,14 +220,8 @@ asm volatile ( \ // step_events_completed reaches block->decelerate_after after which it decelerates until the trapezoid generator is reset. // The slope of acceleration is calculated with the leib ramp alghorithm. -void st_wake_up() { - // TCNT1 = 0; - if(busy == false) - ENABLE_STEPPER_DRIVER_INTERRUPT(); -} - -FORCE_INLINE unsigned short calc_timer(unsigned short step_rate) { - unsigned short timer; +FORCE_INLINE uint16_t calc_timer(uint16_t step_rate) { + uint16_t timer; if(step_rate > MAX_STEP_FREQUENCY) step_rate = MAX_STEP_FREQUENCY; if(step_rate > 20000) { // If steprate > 20kHz >> step 4 times @@ -189,180 +235,267 @@ FORCE_INLINE unsigned short calc_timer(unsigned short step_rate) { else { step_loops = 1; } - + if(step_rate < 32) step_rate = 32; + +#ifdef LOOKUP_TABLE_TIMER step_rate -= 32; // Correct for minimal speed if(step_rate >= (8*256)){ // higher step rate - unsigned short table_address = (unsigned short)&speed_lookuptable_fast[(unsigned char)(step_rate>>8)][0]; + uint16_t table_address = (uint16_t)&speed_lookuptable_fast[(unsigned char)(step_rate>>8)][0]; unsigned char tmp_step_rate = (step_rate & 0x00ff); - unsigned short gain = (unsigned short)pgm_read_word_near(table_address+2); + uint16_t gain = (uint16_t)pgm_read_word_near(table_address+2); MultiU16X8toH16(timer, tmp_step_rate, gain); - timer = (unsigned short)pgm_read_word_near(table_address) - timer; + timer = (uint16_t)pgm_read_word_near(table_address) - timer; } else { // lower step rates - unsigned short table_address = (unsigned short)&speed_lookuptable_slow[0][0]; + uint16_t table_address = (uint16_t)&speed_lookuptable_slow[0][0]; table_address += ((step_rate)>>1) & 0xfffc; - timer = (unsigned short)pgm_read_word_near(table_address); - timer -= (((unsigned short)pgm_read_word_near(table_address+2) * (unsigned char)(step_rate & 0x0007))>>3); + timer = (uint16_t)pgm_read_word_near(table_address); + timer -= (((uint16_t)pgm_read_word_near(table_address+2) * (unsigned char)(step_rate & 0x0007))>>3); } //if(timer < 100) { timer = 100; MSerial.print("Steprate to high : "); MSerial.println(step_rate); }//(20kHz this should never happen) - //TEMPORARY WORKAROUND FOR ZIT BUG - if ( timer > 2000 ) timer = 2000; - return timer; +#else + return (uint16_t)((uint32_t)2000000 / (uint32_t)step_rate); +#endif } -//DEBUGGING -float zadvance; +#ifdef DEBUG_ZADVANCE + volatile float zadvance = 0.0, zadvance2 = 0.0; +#endif + -// Initializes the trapezoid generator from the current block. Called whenever a new -// block begins. -FORCE_INLINE void trapezoid_generator_reset() { - #ifdef ADVANCE - advance = current_block->initial_advance; - final_advance = current_block->final_advance; - // Do E steps + advance steps - e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); - old_advance = advance >>8; - zadvance = advance; +FORCE_INLINE void dda_create(uint8_t ind, bool eAxis) +{ + ddas[ind].eAxis = eAxis; + ddas[ind].counter = 0; + ddas[ind].direction = 1; + ddas[ind].stepperInterfaceDir = false; +} + +FORCE_INLINE void dda_reset(uint8_t ind, bool master, int32_t master_steps, bool direction, int32_t steps, bool keepPhase) +{ + if ( keepPhase ) + { + //Calculate the phase of the new move, keeping the phase of the last move + ddas[ind].counter += ddas[ind].master_steps >> 1; + ddas[ind].counter = (ddas[ind].counter * master_steps) / (ddas[ind].master_steps << 1); + } + else ddas[ind].counter = master_steps >> 1; + +#ifdef OVERSAMPLED_DDA + ddas[ind].counter = - (ddas[ind].counter << OVERSAMPLED_DDA); +#else + ddas[ind].counter = - ddas[ind].counter; +#endif + + ddas[ind].master = master; +#ifdef OVERSAMPLED_DDA + ddas[ind].master_steps = master_steps << OVERSAMPLED_DDA; +#else + ddas[ind].master_steps = master_steps; +#endif + ddas[ind].steps = steps; + ddas[ind].direction = (direction) ? -1 : 1; + ddas[ind].stepperInterfaceDir = (direction) ? false : true; + + ddas[ind].steps_completed = 0; +} + +FORCE_INLINE void dda_shift_phase(uint8_t ind, int32_t phase) +{ +#ifdef OVERSAMPLED_DDA + ddas[ind].counter += phase << OVERSAMPLED_DDA; +#else + ddas[ind].counter += phase; +#endif +} + +FORCE_INLINE void dda_step(uint8_t ind) +{ + ddas[ind].counter += ddas[ind].steps; + if (( ddas[ind].counter > 0 ) && ( ddas[ind].steps_completed < ddas[ind].steps )) + { + ddas[ind].counter -= ddas[ind].master_steps; + +#ifdef JKN_ADVANCE + if ( ddas[ind].eAxis ) + e_steps[current_block->active_extruder] += ddas[ind].direction; + else + { +#endif + stepperInterface[ind].setDirection( ddas[ind].stepperInterfaceDir ); + STEP_TRUE(ind, ddas[ind].stepperInterfaceDir); + stepperInterface[ind].step(false); + count_position[ind] += ddas[ind].direction; +#ifdef JKN_ADVANCE + } +#endif + + ddas[ind].steps_completed ++; + } +} + +// Sets up the next block from the buffer + +FORCE_INLINE void setup_next_block() { +#ifndef CMD_SET_POSITION_CAUSES_DRAIN + memcpy((void *)count_position, current_block->starting_position, sizeof(count_position)); // count_position[] = current_block->starting_position[] +#endif + + #ifdef JKN_ADVANCE + starting_e_position = count_position[E_AXIS]; + + //Something in the buffer, prime if we previously deprimed + if (( deprimed ) && ( current_block->steps[E_AXIS] != 0 )) { + deprimeIndex = current_block->active_extruder; + if ( clockwise_extruder ) { + e_steps[deprimeIndex] -= (int32_t)extruder_deprime_steps; +#ifdef JKN_ADVANCE_LEAD_DE_PRIME + e_steps[deprimeIndex] -= current_block->advance_lead_prime; +#endif + } else { + e_steps[deprimeIndex] += (int32_t)extruder_deprime_steps; +#ifdef JKN_ADVANCE_LEAD_DE_PRIME + e_steps[deprimeIndex] += current_block->advance_lead_prime; +#endif + } + deprimed = false; + } #endif + deceleration_time = 0; + + OCR1A_nominal = calc_timer(current_block->nominal_rate); + step_loops_nominal = step_loops; + // step_rate to timer interval acc_step_rate = current_block->initial_rate; acceleration_time = calc_timer(acc_step_rate); +#ifdef OVERSAMPLED_DDA + OCR1A = acceleration_time >> OVERSAMPLED_DDA; +#else OCR1A = acceleration_time; - OCR1A_nominal = calc_timer(current_block->nominal_rate); - #ifdef Z_LATE_ENABLE - if(current_block->steps_z > 0) stepperInterface[Z_AXIS].setEnabled(true); +#endif + #ifdef ACCELERATION_Z_HOLD_ENABLED + stepperInterface[Z_AXIS].setEnabled(acceleration_zhold || (current_block->steps[Z_AXIS] > 0)); + #endif + + //Setup the next dda's + out_bits = current_block->direction_bits; + for ( uint8_t i = 0; i < NUM_AXIS; i ++ ) + dda_reset(i, (current_block->dda_master_axis_index == i), current_block->step_event_count, (out_bits & (1<steps[i], DDA_KEEP_PHASE); + + #ifdef JKN_ADVANCE + advance_state = ADVANCE_STATE_ACCEL; + #endif + step_events_completed = 0; + + #ifdef DEBUG_BLOCK_BY_MOVE_INDEX + if ( current_block->move_index == 4 ) { + zadvance = (float)current_block->initial_rate; + zadvance2 = (float)current_block->final_rate; + } #endif } + // "The Stepper Driver Interrupt" - This timer interrupt is the workhorse. -// It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. -void st_interrupt() +// It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. +// Returns true if we deleted an item in the pipeline buffer +bool st_interrupt() { + bool block_deleted = false; + +#ifdef OVERSAMPLED_DDA + if ( current_block != NULL ) + { + oversampledCount ++; + if ( oversampledCount < (1 << OVERSAMPLED_DDA) ) + { + //Step the dda for each axis + for (uint8_t i = 0; i < NUM_AXIS; i ++ ) + dda_step(i); + return block_deleted; + } + } +#endif + +// DEBUG_TIMER_START; // If there is no current block, attempt to pop one from the buffer if (current_block == NULL) { // Anything in the buffer? current_block = plan_get_current_block(); if (current_block != NULL) { - trapezoid_generator_reset(); - counter_x = -(current_block->step_event_count >> 1); - counter_y = counter_x; - counter_z = counter_x; - counter_e = counter_x; - step_events_completed = 0; -// #ifdef ADVANCE -// e_steps[current_block->active_extruder] = 0; -// #endif - } - else { + setup_next_block(); + } else { OCR1A=2000; // 1kHz. - } - } - if (current_block != NULL) { - // Set directions TO DO This should be done once during init of trapezoid. Endstops -> interrupt - out_bits = current_block->direction_bits; - - // Set direction en check limit switches - if ((out_bits & (1<steps[E_AXIS] == 0 )) && ( ! deprimed )) { + if ( clockwise_extruder ) { + e_steps[deprimeIndex] += (int32_t)extruder_deprime_steps; +#ifdef JKN_ADVANCE_LEAD_DE_PRIME + e_steps[deprimeIndex] += lastAdvanceDeprime; +#endif + } else { + e_steps[deprimeIndex] -= (int32_t)extruder_deprime_steps; +#ifdef JKN_ADVANCE_LEAD_DE_PRIME + e_steps[deprimeIndex] -= lastAdvanceDeprime; +#endif + } + deprimed = true; + } +#endif - #ifndef ADVANCE - if ((out_bits & (1<steps_e; - if (counter_e > 0) { - counter_e -= current_block->step_event_count; - if ((out_bits & (1<active_extruder]--; - } - else { - e_steps[current_block->active_extruder]++; - } - } - #endif //ADVANCE +#ifdef JKN_ADVANCE + if ( advance_state == ADVANCE_STATE_ACCEL ) { + dda_shift_phase(E_AXIS, current_block->advance_lead_entry); + } + if ( advance_state == ADVANCE_STATE_DECEL ) { + dda_shift_phase(E_AXIS, - current_block->advance_lead_exit); + dda_shift_phase(E_AXIS, - advance_pressure_relax_accumulator >> 8); + } +#endif - counter_x += current_block->steps_x; - if (counter_x > 0) { - stepperInterface[X_AXIS].step(true); - counter_x -= current_block->step_event_count; - stepperInterface[X_AXIS].step(false); - count_position[X_AXIS]+=count_direction[X_AXIS]; - } - - counter_y += current_block->steps_y; - if (counter_y > 0) { - stepperInterface[Y_AXIS].step(true); - counter_y -= current_block->step_event_count; - stepperInterface[Y_AXIS].step(false); - count_position[Y_AXIS]+=count_direction[Y_AXIS]; - } + //Step the dda for each axis + for (uint8_t i = 0; i < NUM_AXIS; i ++ ) + dda_step(i); - counter_z += current_block->steps_z; - if (counter_z > 0) { - stepperInterface[Z_AXIS].step(true); - counter_z -= current_block->step_event_count; - stepperInterface[Z_AXIS].step(false); - count_position[Z_AXIS]+=count_direction[Z_AXIS]; - } +#ifdef OVERSAMPLED_DDA + oversampledCount = 0; +#endif - #ifndef ADVANCE - counter_e += current_block->steps_e; - if (counter_e > 0) { - stepperInterface[E_AXIS].step(true); - counter_e -= current_block->step_event_count; - stepperInterface[E_AXIS].step(false); - count_position[E_AXIS]+=count_direction[E_AXIS]; - } - #endif //!ADVANCE step_events_completed += 1; if(step_events_completed >= current_block->step_event_count) break; } - // Calculare new timer value - unsigned short timer; - unsigned short step_rate; + + // Calculate new timer value + uint16_t timer; if (step_events_completed <= (uint32_t)current_block->accelerate_until) { + // Note that we need to convert acceleration_time from units of + // 2 MHz to seconds. That is done by dividing acceleration_time + // by 2000000. But, that will make it 0 when we use integer + // arithmetic. So, we first multiply block->acceleration_rate by + // acceleration_time and then do the divide. However, it's + // convenient to divide by 2^24 ( >> 24 ). So, block->acceleration_rate + // has been prescaled by a factor of 8.388608. + MultiU24X24toH16(acc_step_rate, acceleration_time, current_block->acceleration_rate); acc_step_rate += current_block->initial_rate; @@ -372,20 +505,33 @@ void st_interrupt() // step_rate to timer interval timer = calc_timer(acc_step_rate); +#ifdef OVERSAMPLED_DDA + OCR1A = timer >> OVERSAMPLED_DDA; +#else OCR1A = timer; +#endif acceleration_time += timer; - #ifdef ADVANCE - for(int8_t i=0; i < step_loops; i++) { - advance += advance_rate; - } - //if(advance > current_block->advance) advance = current_block->advance; - // Do E steps + advance steps - e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); - old_advance = advance >>8; - - #endif } else if (step_events_completed > (uint32_t)current_block->decelerate_after) { +#ifdef JKN_ADVANCE + if ( advance_state == ADVANCE_STATE_ACCEL ) { + advance_state = ADVANCE_STATE_PLATEAU; + } + if ( advance_state == ADVANCE_STATE_PLATEAU ) { + advance_state = ADVANCE_STATE_DECEL; + advance_pressure_relax_accumulator = 0; + } + advance_pressure_relax_accumulator += current_block->advance_pressure_relax; +#endif + + // Note that we need to convert deceleration_time from units of + // 2 MHz to seconds. That is done by dividing deceleration_time + // by 2000000. But, that will make it 0 when we use integer + // arithmetic. So, we first multiply block->acceleration_rate by + // deceleration_time and then do the divide. However, it's + // convenient to divide by 2^24 ( >> 24 ). So, block->acceleration_rate + // has been prescaled by a factor of 8.388608. + MultiU24X24toH16(step_rate, deceleration_time, current_block->acceleration_rate); if(step_rate > acc_step_rate) { // Check step_rate stays positive @@ -393,111 +539,184 @@ void st_interrupt() } else { step_rate = acc_step_rate - step_rate; // Decelerate from aceleration end point. + // lower limit + if(step_rate < current_block->final_rate) + step_rate = current_block->final_rate; } - // lower limit - if(step_rate < current_block->final_rate) - step_rate = current_block->final_rate; - // step_rate to timer interval timer = calc_timer(step_rate); +#ifdef OVERSAMPLED_DDA + OCR1A = timer >> OVERSAMPLED_DDA; +#else OCR1A = timer; +#endif deceleration_time += timer; - #ifdef ADVANCE - for(int8_t i=0; i < step_loops; i++) { - advance -= advance_rate; - } - if(advance < final_advance) advance = final_advance; - // Do E steps + advance steps - e_steps[current_block->active_extruder] += ((advance >>8) - old_advance); - old_advance = advance >>8; - #endif //ADVANCE } else { +#ifdef JKN_ADVANCE + if ( advance_state == ADVANCE_STATE_ACCEL ) { + advance_state = ADVANCE_STATE_PLATEAU; + } +#endif +#ifdef OVERSAMPLED_DDA + OCR1A = OCR1A_nominal >> OVERSAMPLED_DDA; +#else OCR1A = OCR1A_nominal; +#endif + step_loops = step_loops_nominal; } // If current block is finished, reset pointer if (step_events_completed >= current_block->step_event_count) { +#ifdef JKN_ADVANCE + count_position[E_AXIS] = starting_e_position + current_block->steps[E_AXIS]; + starting_e_position = 0; + lastAdvanceDeprime = current_block->advance_lead_deprime; +#endif current_block = NULL; plan_discard_current_block(); + block_deleted = true; + + //Preprocess the setup for the next block if have have one + current_block = plan_get_current_block(); + if (current_block != NULL) { + setup_next_block(); + } +#ifndef CMD_SET_POSITION_CAUSES_DRAIN + else { + //Buffer is empty, as the position may have since changed due to + //plan_set_position, we update our position here + memcpy((void *)count_position, position, sizeof(count_position)); // count_position[] = position + } +#endif } } + return block_deleted; } -#ifdef ADVANCE +#ifdef JKN_ADVANCE void st_advance_interrupt() { - OCR4A = 100 * 16; - + uint8_t i; + + //Increment the rate counters +#ifdef JKN_ADVANCE + for ( i = 0; i < EXTRUDERS; i ++ ) st_advance_interrupt_rate_counter[i] ++; +#endif + // Set E direction (Depends on E direction + advance) - for(unsigned char i=0; i<4;i++) { - if (e_steps[0] != 0) { + for ( i = 0; e_steps[0] && + (st_advance_interrupt_rate_counter[0] >= st_advance_interrupt_rate) && + (i < advance_interrupt_steps_per_call); i ++ ) { stepperInterface[E_AXIS].step(false); if (e_steps[0] < 0) { stepperInterface[E_AXIS].setDirection(false); e_steps[0]++; - stepperInterface[E_AXIS].step(true); + STEP_TRUE(E_AXIS, false); + count_position[E_AXIS]++; } else if (e_steps[0] > 0) { stepperInterface[E_AXIS].setDirection(true); e_steps[0]--; - stepperInterface[E_AXIS].step(true); + STEP_TRUE(E_AXIS, true); + count_position[E_AXIS]--; } - } + st_advance_interrupt_rate_counter[0] = 0; + } #if EXTRUDERS > 1 - if (e_steps[1] != 0) { + for ( i = 0; e_steps[1] && + (st_advance_interrupt_rate_counter[?] >= st_advance_interrupt_rate) && + (i < advance_interrupt_steps_per_call); i ++ ) { //stepperInterface[?].step(false); if (e_steps[1] < 0) { stepperInterface[?].setDirection(false); e_steps[1]++; - //stepperInterface[?].step(true); +// STEP_TRUE(?, false); +// count_position[?]++; } else if (e_steps[1] > 0) { stepperInterface[?].setDirection(true); e_steps[1]--; - //stepperInterface[?].step(true); +// STEP_TRUE(?, true); +// count_position[?]--; } - } + st_advance_interrupt_rate_counter[?] = 0; + } #endif #if EXTRUDERS > 2 - if (e_steps[2] != 0) { + for ( i = 0; e_steps[2] && + (st_advance_interrupt_rate_counter[?] >= st_advance_interrupt_rate) && + (i < advance_interrupt_steps_per_call); i ++ ) { //stepperInterface[?].step(false); if (e_steps[2] < 0) { //stepperInterface[?].setDirection(false); e_steps[2]++; - //stepperInterface[?].step(true); +// STEP_TRUE(?, false); +// count_position[?]++; } else if (e_steps[2] > 0) { //stepperInterface[?].setDirection(true); e_steps[2]--; - //stepperInterface[?].step(true); +// STEP_TRUE(?, true); +// count_position[?]--; } + st_advance_interrupt_rate_counter[?] = 0; } #endif - } } -#endif // ADVANCE +#endif // JKN_ADVANCE void st_init() { + //Create the dda's + dda_create(X_AXIS, false); + dda_create(Y_AXIS, false); + dda_create(Z_AXIS, false); + dda_create(E_AXIS, true); + +#ifdef OVERSAMPLED_DDA + oversampledCount = 0; +#endif + //Grab the stepper interfaces stepperInterface = Motherboard::getBoard().getStepperAllInterfaces(); Motherboard::getBoard().setupAccelStepperTimer(); - #ifdef ADVANCE - e_steps[0] = 0; - e_steps[1] = 0; - e_steps[2] = 0; - #endif //ADVANCE + #ifdef JKN_ADVANCE + deprimed = true; + lastAdvanceDeprime = 0; + #endif + +#ifdef JKN_ADVANCE + //Calculate the smallest number of st_advance_interrupt's between extruder steps based on the the + //extruder_only_max_feedrate and an st_advance_interrupt of 10KHz (ADVANCE_INTERRUPT_FREQUENCY). + float st_advance_interrupt_ratef = (float)ADVANCE_INTERRUPT_FREQUENCY / (FPTOF(extruder_only_max_feedrate) * axis_steps_per_unit[E_AXIS]); + + //Round up (slower), or if we need more steps than 1 in an interrupt (st_advance_interrupt_ratef < 1), then + //we need to process more steps in the loop + if ( st_advance_interrupt_ratef > 1.0 ) st_advance_interrupt_ratef = ceil(st_advance_interrupt_ratef); + else if ( st_advance_interrupt_ratef < 1.0 )st_advance_interrupt_ratef = 0; + st_advance_interrupt_rate = (uint32_t)st_advance_interrupt_ratef; + + advance_interrupt_steps_per_call = 0; + if ( st_advance_interrupt_rate == 0 ) + advance_interrupt_steps_per_call = ceil(1.0 / st_advance_interrupt_ratef); + if ( advance_interrupt_steps_per_call < 1 ) advance_interrupt_steps_per_call = 1; + + for ( uint8_t i = 0; i < EXTRUDERS; i ++ ) { + e_steps[i] = 0; + st_advance_interrupt_rate_counter[i] = st_advance_interrupt_rate; + } +#endif } // Block until all buffered steps are executed bool st_empty() { - if ( blocks_queued() ) return false; - return true; + if (block_buffer_head == block_buffer_tail) return true; + return false; } void st_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e) @@ -507,6 +726,9 @@ void st_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const count_position[Y_AXIS] = y; count_position[Z_AXIS] = z; count_position[E_AXIS] = e; +#ifdef JKN_ADVANCE + starting_e_position = count_position[E_AXIS]; +#endif CRITICAL_SECTION_END; } @@ -514,6 +736,9 @@ void st_set_e_position(const int32_t &e) { CRITICAL_SECTION_START; count_position[E_AXIS] = e; +#ifdef JKN_ADVANCE + starting_e_position = count_position[E_AXIS]; +#endif CRITICAL_SECTION_END; } @@ -530,7 +755,12 @@ void quickStop() { DISABLE_STEPPER_DRIVER_INTERRUPT(); while(blocks_queued()) - plan_discard_current_block(); + plan_discard_current_block(); + current_block = NULL; + plan_set_position((const int32_t)count_position[X_AXIS], + (const int32_t)count_position[Y_AXIS], + (const int32_t)count_position[Z_AXIS], + (const int32_t)count_position[E_AXIS]); ENABLE_STEPPER_DRIVER_INTERRUPT(); } diff --git a/firmware/src/shared/StepperAccel.hh b/firmware/src/shared/StepperAccel.hh index 5f269c0..6da057a 100644 --- a/firmware/src/shared/StepperAccel.hh +++ b/firmware/src/shared/StepperAccel.hh @@ -24,8 +24,57 @@ #include #include "StepperAccelPlanner.hh" +//If defined, we compile in DEBUG_ZADVANCE and doLcd +//#define DEBUG_ZADVANCE + #define MAX_STEP_FREQUENCY 40000 // Max step frequency for Ultimaker (5000 pps / half step) +//If enabled, Z Hold is respected +#define ACCELERATION_Z_HOLD_ENABLED + +//If enabled, endstops are checked during all movement, and movement stops +//if end stop is hit +#define CHECK_ENDSTOPS_ENABLED + +//Enables the debug timer. The timer can detected upto 4ms before overflowing. +//Example usage: +// DEBUG_TIMER_START; +// **** DO SOMETHING **** +// DEBUG_TIMER_FINISH; +// zadvance = DEBUG_TIMER_TCTIMER_CYCLES; + +#define DEBUG_TIMER + +#if defined(DEBUG_TIMER) && defined(TCCR5A) + #define DEBUG_TIMER_START TCCR5A = 0x00;TCCR5B=0x01;TCNT5 = 0 + #define DEBUG_TIMER_FINISH debugTimer = TCNT5 + #define DEBUG_TIMER_TCTIMER_CYCLES ((float)debugTimer) + #define DEBUG_TIMER_TCTIMER_US ((float)debugTimer / 16.0) //16 = cpu frequency / 1000000 + #define DEBUG_TIMER_TCTIMER_USI (debugTimer / 16) //16 = cpu frequency / 1000000 + extern uint16_t debugTimer; +#else + #define DEBUG_TIMER_START + #define DEBUG_TIMER_FINISH + #define DEBUG_TIMER_TCTIMER_CYCLES 0 + #define DEBUG_TIMER_TCTIMER_US 0.0 + #define DEBUG_TIMER_TCTIMER_USI 0 +#endif + + +//If defined, the speed lookup table is used to calculate the timer +//otherwise, the timer is calculated with a divide. + +// Don't use the lookup table when simulating -- simulator doesn't +// bother to provide a pgm mem library emulator +#ifndef SIMULATOR +#define LOOKUP_TABLE_TIMER +#endif + +//How many periods we lookback to calculate the average feedrate +//This number must be an integer value +#define STATS_EMA_PERIODS 20.0 + + #ifndef CRITICAL_SECTION_START #define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli(); #define CRITICAL_SECTION_END SREG = _sreg; @@ -33,12 +82,7 @@ enum AxisEnum {X_AXIS=0, Y_AXIS=1, Z_AXIS=2, E_AXIS=3}; - -// of the buffer and all stops. This should not be much greater than zero and should only be changed -// if unwanted behavior is observed on a user's machine when running at very slow speeds. -#define MINIMUM_PLANNER_SPEED 2.0 // (mm/sec) - -const int dropsegments=5; //everything with less than this number of steps will be ignored as move and joined with the next movement +const uint16_t dropsegments=0; //everything with less than this number of steps will be ignored as move and joined with the next movement #define EXTRUDERS 1 @@ -55,18 +99,20 @@ void st_set_e_position(const int32_t &e); // Get current position in steps int32_t st_get_position(uint8_t axis); -// The stepper subsystem goes to sleep when it runs out of things to execute. Call this -// to notify the subsystem that it is time to go to work. -void st_wake_up(); - -void st_interrupt(); +// Returns true if we deleted an item in the pipeline buffer +bool st_interrupt(); void st_advance_interrupt(); extern block_t *current_block; // A pointer to the block currently being traced +extern int32_t extruder_deprime_steps; +extern bool clockwise_extruder; + void quickStop(); //DEBUGGING -extern float zadvance; +#ifdef DEBUG_ZADVANCE + extern volatile float zadvance, zadvance2; +#endif #endif diff --git a/firmware/src/shared/StepperAccelPlanner.cc b/firmware/src/shared/StepperAccelPlanner.cc index b4c1ce5..48b1afa 100644 --- a/firmware/src/shared/StepperAccelPlanner.cc +++ b/firmware/src/shared/StepperAccelPlanner.cc @@ -50,7 +50,14 @@ IntersectionDistance[s1_, s2_, a_, d_] := (2 a d - s1^2 + s2^2)/(4 a) */ - + +#ifdef SIMULATOR +#include +#include +#include +#include "Simulator.hh" +#endif + #include "Configuration.hh" #ifdef HAS_STEPPER_ACCELERATION @@ -60,10 +67,14 @@ #include #include #include +#ifndef SIMULATOR #include +#endif #include "StepperAccel.hh" +#ifndef SIMULATOR #include "StepperInterface.hh" #include "Motherboard.hh" +#endif #ifdef abs #undef abs @@ -73,49 +84,194 @@ #define max(a,b) ((a)>(b)?(a):(b)) #define abs(x) ((x)>0?(x):-(x)) -#define ZSQUARE(x) ((x)*(x)) - #define VEPSILON 1.0e-5 // v1 != v2 -#define VNEQ(v1,v2) (abs((v1)-(v2)) > VEPSILON) +#ifdef FIXED + #define VNEQ(v1,v2) ((v1) != (v2)) + #define VLT(v1,v2) ((v1) < (v2)) +#else + #define VNEQ(v1,v2) (abs((v1)-(v2)) > VEPSILON) + #define VLT(v1,v2) (((v1) + VEPSILON) < (v2)) +#endif //=========================================================================== //=============================public variables ============================ //=========================================================================== uint32_t minsegmenttime; -float max_feedrate[4]; // set the max speeds -float axis_steps_per_unit[4]; -uint32_t max_acceleration_units_per_sq_second[4]; // Use M201 to override by software -float minimumfeedrate; -float p_acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX -float p_retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX -float max_xy_jerk; //speed than can be stopped at once, if i understand correctly. -float max_xy_jerk_squared; // max_xy_jerk * max_xy_jerk -float max_z_jerk; -float mintravelfeedrate; +FPTYPE max_feedrate[NUM_AXIS]; // set the max speeds +uint32_t max_acceleration_units_per_sq_second[NUM_AXIS]; // Use M201 to override by software +FPTYPE minimumfeedrate; +FPTYPE p_acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX +FPTYPE p_retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX +FPTYPE smallest_max_speed_change; +FPTYPE max_speed_change[NUM_AXIS]; //The speed between junctions in the planner, reduces blobbing +FPTYPE mintravelfeedrate; +FPTYPE minimumPlannerSpeed; +FPTYPE extruder_only_max_feedrate; +int slowdown_limit; +float axis_steps_per_unit[NUM_AXIS]; + +bool disable_slowdown = true; uint32_t axis_steps_per_sqr_second[NUM_AXIS]; -float extrution_area, extruder_advance_k, steps_per_cubic_mm_e; +#ifdef JKN_ADVANCE +FPTYPE extruder_advance_k = 0, extruder_advance_k2 = 0; +#endif +FPTYPE delta_mm[NUM_AXIS]; +FPTYPE planner_distance; +uint32_t planner_master_steps; +int32_t planner_steps[NUM_AXIS]; +FPTYPE vmax_junction; +FPTYPE cosine; +#ifndef FIXED +#ifndef YET_ANOTHER_JERK + FPTYPE compareLessThanEqualZero = 0.001; +#endif +#endif + +// minimum time in seconds that a movement needs to take if the buffer is emptied. +// Increase this number if you see blobs while printing high speed & high detail. +// It will slowdown on the detailed stuff. +// Comment out to disable +FPTYPE minimumSegmentTime; // The current position of the tool in absolute steps -int32_t position[4]; //rescaled from extern when axis_steps_per_unit are changed by gcode -static float previous_speed[4]; // Speed of previous path line segment -static float previous_nominal_speed; // Nominal speed of previous path line segment +int32_t position[NUM_AXIS]; //rescaled from extern when axis_steps_per_unit are changed by gcode +#ifndef YET_ANOTHER_JERK +static FPTYPE previous_delta_mm[NUM_AXIS]; +#else +static FPTYPE prev_speed[NUM_AXIS]; +#endif +static FPTYPE previous_inverse_millimeters; + +#ifndef SIMULATOR static StepperInterface *stepperInterface; +#else +static block_t *sblock = NULL; +static char prior_dropped[1024] = "\0"; +#endif +bool acceleration_zhold = false; + +#ifdef DEBUG_BLOCK_BY_MOVE_INDEX + uint32_t current_move_index = 0; +#endif + +#ifdef FIXED +#ifndef SIMULATOR + +//http://members.chello.nl/j.beentjes3/Ruud/sqrt32avr.htm + +static int16_t isqrt1(int16_t value) +{ + int16_t result; + + asm volatile ( +"; Fast and short 16 bits AVR sqrt routine" "\n\t" +";" "\n\t" +"; R17:R16 = sqrt(R3:R2), rounded down to integer" "\n\t" +";" "\n\t" +"; Registers:" "\n\t" +"; Destroys the argument in R3:R2" "\n\t" +";" "\n\t" +"; Cycles incl call & ret = 90 - 96" "\n\t" +";" "\n\t" +"; Stack incl call = 2" "\n\t" +"Sqrt16: ldi %B0,0xc0 ; Rotation mask register" "\n\t" +" ldi %A0,0x40 ; Developing sqrt" "\n\t" +" clc ; Enter loop with C=0" "\n\t" +"_sq16_1: brcs _sq16_2 ; C --> Bit is always 1" "\n\t" +" cp %B1,%A0 ; Does value fit?" "\n\t" +" brcs _sq16_3 ; C --> bit is 0" "\n\t" +"_sq16_2: sub %B1,%A0 ; Adjust argument for next bit" "\n\t" +" or %A0,%B0 ; Set bit to 1" "\n\t" +"_sq16_3: lsr %B0 ; Shift right rotation mask" "\n\t" +" lsl %A1" "\n\t" +" rol %B1 ; Shift left argument, C --> Next sub is MUST" "\n\t" +" eor %A0,%B0 ; Shift right test bit in developing sqrt" "\n\t" +" andi %B0,0xfe ; Becomes 0 for last bit" "\n\t" +" brne _sq16_1 ; Develop 7 bits" "\n\t" +" brcs _sq16_4 ; C--> Last bit always 1" "\n\t" +" lsl %A1 ; Need bit 7 in C for cpc" "\n\t" +" cpc %A0,%B1 ; After this C is last bit" "\n\t" +"_sq16_4: adc %A0,%B0 ; Set last bit if C (R17=0)" "\n\t" + + : "=&r" (result) + : "r" (value) + ); + return result; +} + +#endif +#endif + +#if 0 +//http://avr.15.forumer.com/a/fast-squareroot-routine-avr-asm_post264.html + +static uint8_t isqrt2(uint16_t value) +{ + uint8_t result; + + asm volatile ( +"; RETURN:" "\n\t" +"; return value: RetVal ( = sqrt(InputH:InputL))" "\n\t" +"; remainder: SREG(t):InputH ( = InputH:InputL - (sqrt(InputH:InputL))^2)" "\n\t" +"; (SREG(t) - MSB (8th) of remainder)" "\n\t" + +"Sqrt16:" "\n\t" +"clr %0" "\n\t" +"clr __tmp_reg__" "\n\t" +"ldi r19,(1 << 7)" "\n\t" +"clt" "\n\t" + +"Sqrt16Loop:" "\n\t" +"mov r20,r19" "\n\t" +"lsr r20" "\n\t" +"ror __tmp_reg__" "\n\t" +"or r20,%0" "\n\t" + +"brts Sqrt16Sub" "\n\t" + +"cp %A1,__tmp_reg__" "\n\t" +"cpc %B1,r20" "\n\t" +"brcs Sqrt16NoSub" "\n\t" + +"Sqrt16Sub:" "\n\t" +"sub %A1,__tmp_reg__" "\n\t" +"sbc %B1,r20" "\n\t" +"or %0,r19" "\n\t" + +"Sqrt16NoSub:" "\n\t" +"bst %B1,7" "\n\t" +"lsl %A1" "\n\t" +"rol %B1" "\n\t" + +"lsr r19" "\n\t" +"brcc Sqrt16Loop" "\n\t" + : "=&r" (result) + : "r" (value) + : "r20", "r19" + ); + return result; +} + +#endif //=========================================================================== //=================semi-private variables, used in inline functions ===== //=========================================================================== block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions -volatile unsigned char block_buffer_head; // Index of the next block to be pushed -volatile unsigned char block_buffer_tail; // Index of the block to process now +volatile unsigned char block_buffer_head = 0; // Index of the next block to be pushed +volatile unsigned char block_buffer_tail = 0; // Index of the block to process now // Returns the index of the next block in the ring buffer // NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication. -static int8_t next_block_index(int8_t block_index) { +#ifndef SAVE_SPACE +FORCE_INLINE +#endif +static uint8_t next_block_index(uint8_t block_index) { block_index++; if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; } return(block_index); @@ -123,7 +279,10 @@ static int8_t next_block_index(int8_t block_index) { // Returns the index of the previous block in the ring buffer -static int8_t prev_block_index(int8_t block_index) { +#ifndef SAVE_SPACE +FORCE_INLINE +#endif +static uint8_t prev_block_index(uint8_t block_index) { if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; } block_index--; return(block_index); @@ -135,14 +294,57 @@ static int8_t prev_block_index(int8_t block_index) { // Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the // given acceleration: -FORCE_INLINE float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration) + +// Note the equation used below is EXACT: there's no "estimation" involved. +// As such, the name is a bit misleading. + +// t = time +// a = acceleration (constant) +// d(t) = distance travelled at time t +// initial_rate = rate of travel at time t = 0 +// rate(t) = rate of travel at time t +// +// From basic kinematics, we have +// +// [1] d(t) = d(0) + initial_rate * t + 0.5 * a * t^2, +// +// and +// +// [2] rate(t) = initial_rate + a * t. +// +// For our purposes, d(0) +// +// [3] d(0) = 0. +// +// Solving [2] for time t, gives us +// +// [4] t = ( rate(t) - initial_rate ) / a. +// +// Substituting [3] and [4] into [1] produces, +// +// [5] d(t) = initial_rate * ( rate(t) - intial_rate ) / a + ( rate(t) - initial_rate )^2 / 2a. +// +// With some algebraic simplification, we then find that d(t) is given by +// +// [6] d(t) = ( rate(t)^2 - initial_rate^2 ) / 2a. +// +// So, if we know our desired initial rate, initial_rate, and our acceleration, the distance d +// required to reach a target rate, target_rate, is then +// +// [7] d = ( target_rate^2 - initial_rate^2 ) / 2a. +// +// Note that if the acceleration is 0, we can never reach the target rate unless the +// initial and target rates are the same. This stands to reason since if the acceleration +// is zero, then our speed never changes and thus no matter how far we move, we're still +// moving at our initial speed. + +FORCE_INLINE int32_t estimate_acceleration_distance(int32_t initial_rate_sq, int32_t target_rate_sq, int32_t acceleration_doubled) { - if (acceleration!=0) { - return((target_rate*target_rate-initial_rate*initial_rate)/ - (2.0*acceleration)); + if (acceleration_doubled!=0) { + return((target_rate_sq-initial_rate_sq)/acceleration_doubled); } else { - return 0.0; // acceleration was 0, set acceleration distance to 0 + return 0; // acceleration was 0, set acceleration distance to 0 } } @@ -151,33 +353,183 @@ FORCE_INLINE float estimate_acceleration_distance(float initial_rate, float targ // a total travel of distance. This can be used to compute the intersection point between acceleration and // deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) -FORCE_INLINE float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance) +// +// accelerate +a decelerate -a +// |<---- d1 ---->|<---------- d2 ---------->| +// |<------------- d = d1 + d2 ------------->| +// t=0 t=t1 t=t1+t2 +// initial_rate peak_rate final_rate +// +// From basic kinematics, +// +// [1] d1 = initial_rate t1 + 0.5 a t1^2 +// [2] d2 = final_rate t2 + 0.5 a t2^2 [think of starting at speed final_rate and accelerating by a] +// [3] final_rate = intial_rate + a (t1 - t2) +// [4] d2 = d - d1 +// +// We wish to solve for d1 given a, d, initial_rate, and final_rate. +// By the quadratic equation, +// +// [5] t1 = [ -initial_rate +/- sqrt( initial_rate^2 + 2 a d1 ) ] / a +// [6] t2 = [ -final_rate +/- sqrt( final_rate^2 + 2 a d2 ) ] / a +// +// Replacing t1 and t2 in [6] then produces, +// +// [7] final_rate = initial_rate - initial_rate +/- sqrt( initial_rate^2 + 2 a d1 ) + +// + final_rate -/+ sqrt( final_rate^2 + 2 a d2 ) +// +// And thus, +// +// [8] +/- sqrt( initial_rate^2 + 2 a d1 ) = +/- sqrt( final_rate^2 + 2 a d2 ) +// +// Squaring both sides and substituting d2 = d - d1 [4] yields, +// +// [9] initial_rate^2 + 2 a d1 = final_rate^2 + 2 a d - 2 a d1 +// +// Solving [9] for d1 then gives our desired result, +// +// [10] d1 = ( final_rate^2 - initial_rate^2 + 2 a d ) / 4a + +FORCE_INLINE int32_t intersection_distance(int32_t initial_rate_sq, int32_t final_rate_sq, int32_t acceleration_doubled, int32_t distance) { - if (acceleration!=0) { - return((2.0*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/ - (4.0*acceleration) ); + if (acceleration_doubled!=0) { + return((acceleration_doubled*distance-initial_rate_sq+final_rate_sq)/(acceleration_doubled << 1) ); } else { - return 0.0; // acceleration was 0, set intersection distance to 0 + return 0; // acceleration was 0, set intersection distance to 0 } } + +#ifdef JKN_ADVANCE + +//Same as final_speed, except this one works with step_rates. +//Regular final_speed will overflow if we use step_rates instead of mm/s + +FORCE_INLINE FPTYPE final_speed_step_rate(uint32_t acceleration, uint32_t initial_velocity, int32_t distance) { + uint32_t v2 = initial_velocity * initial_velocity; +#ifdef SIMULATOR + uint64_t sum2 = (uint64_t)initial_velocity * (uint64_t)initial_velocity + + 2 * (uint64_t)acceleration * (uint64_t)distance; + float fres = (sum2 > 0) ? sqrt((float)sum2) : 0.0; + FPTYPE result; +#endif + //Although it's highly unlikely, if target_rate < initial_rate, then distance could be negative. + if ( distance < 0 ) { + uint32_t term2 = (acceleration * (uint32_t)abs(distance)) << 1; + + if ( term2 >= v2 ) return 0; + v2 -= term2; + } + else v2 += (acceleration * (uint32_t)distance) << 1; + if (v2 <= 0x7fff) +#ifndef SIMULATOR + return ITOFP(isqrt1((uint16_t)v2)); +#else +#define isqrt1(x) ((int32_t)sqrt((float)(x))) + result = ITOFP(isqrt1((uint16_t)v2)); +#endif + else + { + uint8_t n = 0; + while (v2 > 0x7fff) + { + v2 >>= 2; + n++; + } +#ifndef SIMULATOR + return ITOFP(isqrt1((int16_t)v2)) << n; +#else + result = ITOFP(isqrt1((int16_t)v2)) << n; +#endif + } +#ifdef SIMULATOR + if ((fres != 0.0) && ((fabsf(fres - FPTOF(result))/fres) > 0.01)) + { + char buf[1024]; + snprintf(buf, sizeof(buf), + "*** final_speed_step_rate(%d, %d, %d): fixed result = %f; " + "float result = %f", acceleration, initial_velocity, distance, + FPTOF(result), fres); + if (sblock) + { + if (sblock->message[0] != '\0') + strncat(sblock->message, "\n", sizeof(sblock->message)); + strncat(sblock->message, buf, sizeof(sblock->message)); + } + else + printf("%s\n", buf); + } + return result; +#endif +} + +#endif + // Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors. -void calculate_trapezoid_for_block(block_t *block, float entry_factor, float exit_factor) { - uint32_t initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min) - uint32_t final_rate = ceil(block->nominal_rate*exit_factor); // (step/min) +void calculate_trapezoid_for_block(block_t *block, FPTYPE entry_factor, FPTYPE exit_factor) { + + // If exit_factor or entry_factor are larger than unity, then we will scale + // initial_rate or final_rate to exceed nominal_rate. However, maximum feed rates + // have been applied to nominal_rate and as such we should not exceed nominal_rate. + + // For example, if the next block's entry_speed exceeds this block's nominal_speed, + // then we can be called with exit_factor = next->entry_speed / current->nominal_speed > 1. + // Basically, that's saying that the next block's nominal speed which was subject to + // per axis feed rates for a different combination of axes steps can override this block's + // speed limits. We don't want that. For example, if this block's motion is primarily + // Z or E axis steps and the next block's is all X axis steps, we don't want the next + // block's X-axis limited entry_speed telling this block that it's Z or E axis limited + // final_rate can be increased past the block's nominal_rate. + + if ( (!block->use_accel) || (entry_factor > KCONSTANT_1) ) entry_factor = KCONSTANT_1; + if ( (!block->use_accel) || (exit_factor > KCONSTANT_1) ) exit_factor = KCONSTANT_1; + +#ifdef FIXED + #ifdef NO_CEIL_FLOOR + uint32_t initial_rate = (uint32_t)FPTOI(KCONSTANT_0_5 + FPMULT2(ITOFP(block->nominal_rate), entry_factor)); // (step/min) + uint32_t final_rate = (uint32_t)FPTOI(KCONSTANT_0_5 + FPMULT2(ITOFP(block->nominal_rate), exit_factor)); // (step/min) + #else + uint32_t initial_rate = (uint32_t)FPTOI(FPCEIL(FPMULT2(ITOFP(block->nominal_rate), entry_factor))); // (step/min) + uint32_t final_rate = (uint32_t)FPTOI(FPCEIL(FPMULT2(ITOFP(block->nominal_rate), exit_factor))); // (step/min) + #endif +#else + #ifdef NO_CEIL_FLOOR + uint32_t initial_rate = (uint32_t)(0.5 + block->nominal_rate*entry_factor); // (step/min) + uint32_t final_rate = (uint32_t)(0.5 + block->nominal_rate*exit_factor); // (step/min) + #else + uint32_t initial_rate = FPCEIL(block->nominal_rate*entry_factor); // (step/min) + uint32_t final_rate = FPCEIL(block->nominal_rate*exit_factor); // (step/min) + #endif +#endif + + // If we really need to squeeze cyles, then we can instead do the following + // but then we'd be testing for rates < 128 + // if (0 == (initial_rate & 0xffffff80)) initial_rate=127; // initial_rate < 127 + // if (0 == (final_rate & 0xffffff80)) final_rate=127; // final_rate < 127 // Limit minimal step rate (Otherwise the timer will overflow.) if(initial_rate <120) {initial_rate=120; } if(final_rate < 120) {final_rate=120; } + + int32_t initial_rate_sq = (int32_t)(initial_rate * initial_rate); + int32_t nominal_rate_sq = (int32_t)(block->nominal_rate * block->nominal_rate); + int32_t final_rate_sq = (int32_t)(final_rate * final_rate); int32_t acceleration = block->acceleration_st; - int32_t accelerate_steps = - ceil(estimate_acceleration_distance(block->initial_rate, block->nominal_rate, acceleration)); - int32_t decelerate_steps = - floor(estimate_acceleration_distance(block->nominal_rate, block->final_rate, -acceleration)); - + int32_t acceleration_doubled = acceleration << 1; + int32_t accelerate_steps = 0; + int32_t decelerate_steps = 0; + if ( block->use_accel ) + { + accelerate_steps = estimate_acceleration_distance(initial_rate_sq, nominal_rate_sq, acceleration_doubled); + decelerate_steps = estimate_acceleration_distance(nominal_rate_sq, final_rate_sq, -acceleration_doubled); + } + + // accelerate_steps = max(accelerate_steps,0); // Check limits due to numerical round-off + // accelerate_steps = min(accelerate_steps,(int32_t)block->step_event_count); + // Calculate the size of Plateau of Nominal Rate. int32_t plateau_steps = block->step_event_count-accelerate_steps-decelerate_steps; @@ -185,42 +537,429 @@ void calculate_trapezoid_for_block(block_t *block, float entry_factor, float exi // have to use intersection_distance() to calculate when to abort acceleration and start braking // in order to reach the final_rate exactly at the end of this block. if (plateau_steps < 0) { - accelerate_steps = ceil( - intersection_distance(block->initial_rate, block->final_rate, acceleration, block->step_event_count)); + accelerate_steps = intersection_distance(initial_rate_sq, final_rate_sq, acceleration_doubled, (int32_t)block->step_event_count); accelerate_steps = max(accelerate_steps,0); // Check limits due to numerical round-off - accelerate_steps = min(accelerate_steps,block->step_event_count); + accelerate_steps = min(accelerate_steps,(int32_t)block->step_event_count); plateau_steps = 0; } + int32_t decelerate_after = accelerate_steps + plateau_steps; + + #ifdef JKN_ADVANCE + #ifdef SIMULATOR + sblock = block; + #endif + int32_t advance_lead_entry = 0, advance_lead_exit = 0, advance_lead_prime = 0, advance_lead_deprime = 0; + int32_t advance_pressure_relax = 0; + + if ( block->use_advance_lead ) { + uint32_t maximum_rate; + + // Note that we accelerate between step 0 & 1, between 1 & 2, ..., between + // acclerate_steps-1 & accelerate_steps, AND accelerate_steps & accelerate_steps+1 + // So, we accelerate for accelerate_steps + 1. This is because in st_interrupt() + // the test is "if (step_events_completed <= accelerate_until)" which means that + // between step accelerate_until and the next step, we're still doing acceleration. + + if ( plateau_steps == 0 ) maximum_rate = FPTOI(final_speed_step_rate(block->acceleration_st, initial_rate, + accelerate_steps + 1)); + else maximum_rate = block->nominal_rate; + + // Don't waste cycles computing these values if we won't use them in st_interrupt() + if (accelerate_steps > 0) { +#ifdef JKN_ADVANCE_LEAD_ACCEL + advance_lead_entry = FPTOI(FPMULT2(extruder_advance_k, ITOFP((int32_t)block->acceleration_st>>4))); +#else + // Acceleration dependent portion + + // Basis of computation is as follows. Suppose the filament velocity, Vf, should be + // + // [1] Vf = C1 * Vn + C2 * a + // + // where + // + // Vn = extruded noodle velocity + // a = acceleration + // C1 = constant (ratio of volumes and whatnot) + // C2 = another constant + // + // But we're normally just taking Vf = C1 * Vn and thus we're missing a contribution + // associated with acceleration (e.g., a contribution associated with energy loss to + // friction in the extruder nozzle). We can then ask, well how many e-steps do we + // miss as a result? Using, + // + // [2] distance in e-space = velocity x time + // + // we would then appear to be missing + // + // [3] delta-e = C2 * a * time-spent-accelerating + // + // From basic kinematics, we know the time spent accelerating under constant acceleration, + // + // [4] Vfinal = Vinitial + a * time-spent-accelerating + // + // and thus + // + // [5] time-spent-accelerating = (Vfinal - Vinitial) / a + // + // Substituting [5] into [3] yields, + // + // [6] delta-e = C2 * (Vfinal - Vinitial) + // + // where Vinitial and Vfinal are the initial and final speeds + // at the start and end of the acceleration or deceleration phase. + + advance_lead_entry = FPTOI(FPMULT2(extruder_advance_k, ITOFP((int32_t)(maximum_rate - initial_rate)))); +#endif + +#ifdef JKN_ADVANCE_LEAD_DE_PRIME + // Prime. Same as advance_lead_entry, but for when we're priming from 0 to initial_rate + // Note that we may or may not use this value, it's only used if the previous segment was not available or had e steps of 0 + advance_lead_prime = FPTOI(FPMULT2(extruder_advance_k, ITOFP((int32_t)initial_rate)) - KCONSTANT_0_5); +#endif + +#ifndef SIMULATOR + if (advance_lead_entry < 0) advance_lead_entry = 0; + if (advance_lead_prime < 0) advance_lead_prime = 0; +#endif + } + if ((decelerate_after+1) < (int32_t)block->step_event_count) { +#ifdef JKN_ADVANCE_LEAD_ACCEL + advance_lead_exit = FPTOI(FPMULT2(extruder_advance_k, ITOFP((int32_t)block->acceleration_st>>4))); +#else + // Acceleration dependent portion + advance_lead_exit = FPTOI(FPMULT2(extruder_advance_k, ITOFP((int32_t)(maximum_rate - final_rate)))); +#endif + +#ifdef JKN_ADVANCE_LEAD_DE_PRIME + // Deprime. Same as advance_lead_exit, but for when we're depriming from final_rate to 0 + // Note that we may or may not use this value, it's only used if the next segment is not available or has e steps of 0 + advance_lead_deprime = FPTOI(FPMULT2(extruder_advance_k, ITOFP((int32_t)final_rate)) - KCONSTANT_0_5); +#endif - #ifdef ADVANCE - volatile int32_t initial_advance = block->advance*entry_factor*entry_factor; - volatile int32_t final_advance = block->advance*exit_factor*exit_factor; - #endif // ADVANCE + //Pressure relaxation + if ( extruder_advance_k2 != 0 ) { + advance_pressure_relax = FPTOI(FPDIV(FPMULT3(extruder_advance_k2, ITOFP((int32_t)block->acceleration_st >> 4), KCONSTANT_100), ITOFP((int32_t)decelerate_steps))); + } + +#ifndef SIMULATOR + //If we've overflowed, reset to 0 + if ( advance_pressure_relax < 0 ) advance_pressure_relax = 0; + + if (advance_lead_exit < 0) advance_lead_exit = 0; + if (advance_lead_deprime < 0) advance_lead_deprime = 0; +#endif + } +#ifdef SIMULATOR + if ( (advance_lead_entry < 0) || (advance_lead_exit < 0) || (advance_pressure_relax < 0) ) { + char buf[1024]; + snprintf(buf, sizeof(buf), + "*** calculate_trapezoid_for_block(): advance_lead_entry=%d, advance_lead_exit=%d, " + "advance_pressure_relax=%d; initial/nominal/maximum/final_rate=%d/%d/%d/%d; " + "accelerate_until/decelerate_after/step_events/plateau_steps=%d/%d/%d/%d; " + "i/n/f/a=%d/%d/%d/%d\n", + advance_lead_entry, advance_lead_exit, advance_pressure_relax,initial_rate, block->nominal_rate, + maximum_rate, final_rate, accelerate_steps, decelerate_after, block->step_event_count, + plateau_steps, initial_rate_sq, nominal_rate_sq, final_rate_sq, acceleration_doubled); + if (block->message[0] != '\0') + strncat(block->message, "\n", sizeof(block->message)); + strncat(block->message, buf, sizeof(block->message)); + } +#endif + } + #endif - // block->accelerate_until = accelerate_steps; - // block->decelerate_after = accelerate_steps+plateau_steps; CRITICAL_SECTION_START; // Fill variables used by the stepper in a critical section if(block->busy == false) { // Don't update variables if block is busy. block->accelerate_until = accelerate_steps; - block->decelerate_after = accelerate_steps+plateau_steps; + block->decelerate_after = decelerate_after; block->initial_rate = initial_rate; block->final_rate = final_rate; - #ifdef ADVANCE - block->initial_advance = initial_advance; - block->final_advance = final_advance; - #endif //ADVANCE + #ifdef JKN_ADVANCE + block->advance_lead_entry = advance_lead_entry; + block->advance_lead_exit = advance_lead_exit; + block->advance_lead_prime = advance_lead_prime; + block->advance_lead_deprime = advance_lead_deprime; + block->advance_pressure_relax = advance_pressure_relax; + #endif } CRITICAL_SECTION_END; +#ifdef SIMULATOR + block->planned += 1; +#endif } +#ifndef FIXSIGN + // Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the // acceleration within the allotted distance. -FORCE_INLINE float max_allowable_speed(float acceleration, float target_velocity, float distance) { - return sqrt(target_velocity*target_velocity-2*acceleration*distance); +FORCE_INLINE FPTYPE max_allowable_speed(FPTYPE acceleration, FPTYPE target_velocity, FPTYPE distance) { +#ifdef FIXED + FPTYPE v2 = FPSQUARE(target_velocity) - (FPMULT2(acceleration, distance) << 1); +#else + FPTYPE v2 = FPSQUARE(target_velocity) - 2.0 * acceleration * distance; +#endif + if (v2 < 0) return (0); + else return FPSQRT(v2); +} + +#else + +// Calculates the speed you must start at in order to reach target_velocity using the +// acceleration within the allotted distance. +// +// See final_speed() for a derivation of this code. For initial_speed(), "-a" should +// be used in place of "a" for the acceleration. And the target_velocity used in place +// of the initial_velocity. +// +// Note bene: if the distance or acceleration is sufficiently large, then there's +// no initial speed which will work. The acceleration is so large that the +// the target velocity will be attained BEFORE the distance is covered. As +// such even an initial speed of zero won't work. When this happens, the value +// under the square root is negative. In that case, we simply return a value +// of zero. + +FORCE_INLINE FPTYPE initial_speed(FPTYPE acceleration, FPTYPE target_velocity, FPTYPE distance) { +#ifdef FIXED +#ifdef SIMULATOR + FPTYPE acceleration_original = acceleration; + FPTYPE distance_original = distance; + FPTYPE target_velocity_original = target_velocity; + float ftv = FPTOF(target_velocity); + float fac = FPTOF(acceleration); + float fd = FPTOF(distance); + float fres = ftv * ftv - 2.0 * fac * fd; + if (fres <= 0.0) fres = 0.0; + else fres = sqrt(fres); +#endif + + // We wish to compute + // + // sum2 = target_velocity * target_velocity - 2 * acceleration * distance + // + // without having any overflows. We therefore divide everything in + // site by 2^12. After computing sqrt(sum2), we will then multiply + // the result by 2^6 (the square root of 2^12). + + target_velocity >>= 12; + acceleration >>= 6; + distance >>= 5; // 2 * (distance >> 6) + FPTYPE sum2 = FPSQUARE(target_velocity) - FPMULT2(distance, acceleration); + + // Now, comes the real speed up: use our fast 16 bit integer square + // root (in assembler nonetheles). To pave the way for this, we shift + // sum2 as much to the left as possible thereby maximizing the use of + // the "whole" or "integral" part of the fixed point number. We then + // take the square root of the integer part of sum2 which has been + // multiplied by 2^(2n) [n left shifts by 2]. After taking the square + // root, we correct this scaling by dividing the result by 2^n (which + // is the square root of 2^(2n) + + uint8_t n = 0; + while ((sum2 != 0) && (sum2 & 0xe0000000) == 0) { + sum2 <<= 2; + n++; + } + +#ifndef SIMULATOR + // Generate the final result. We need to undo two sets of + // scalings: our original division by 2^12 which we rectify + // by multiplying by 2^6. But also we need to divide by 2^n + // so as to counter the 2^(2n) scaling we did. This means + // a net multiply by 2^(6-n). + if (sum2 <= 0) return 0; + else if (n > 6) return ITOFP(isqrt1(FPTOI16(sum2))) >> (n - 6); + else return ITOFP(isqrt1(FPTOI16(sum2))) << (6 - n); +#else + FPTYPE result; + if (sum2 <= 0) result = 0; + else result = ITOFP(isqrt1(FPTOI16(sum2))); + if (n > 6) result >>= (n - 6); + else result <<= (6 - n); + if ((fres != 0.0) && ((fabsf(fres - FPTOF(result))/fres) > 0.05)) + { + char buf[1024]; + snprintf(buf, sizeof(buf), + "*** initial_speed(%f, %f, %f): fixed result = %f; " + "float result = %f", + FPTOF(acceleration_original), + FPTOF(target_velocity_original), + FPTOF(distance_original), + FPTOF(result), fres); + if (sblock) + { + if (sblock->message[0] != '\0') + strncat(sblock->message, "\n", sizeof(sblock->message)); + strncat(sblock->message, buf, sizeof(sblock->message)); + } + else + printf("%s\n", buf); + } + return result; +#endif // SIMULATOR +#else + FPTYPE v2 = FPSQUARE(target_velocity) - 2.0 * acceleration * distance; + if (v2 <= 0) return 0; + else return FPSQRT(v2); +#endif // !FIXED } +// Calculates the final speed (terminal speed) which will be attained if we start at +// speed initial_velocity and then accelerate at the given rate over the given distance +// From basic kinematics, we know that displacement d(t) at time t for an object moving +// initially at speed v0 and subject to constant acceleration a is given by +// +// [1] d(t) = v0 t + 0.5 a t^2, t >= 0 +// +// We also know that the speed v(t) at time t is governed by +// +// [2] v(t) = v0 + a t +// +// Now, without reference to time, we desire to find the speed v_final we will +// find ourselves moving at after travelling a given distance d. To find an answer +// to that question, we need to solve one of the two above equations for t and +// then substitute the result into the remaining equation. As it turns out, the +// algebra is a little easier whne [1] is solved for t using the quadratic +// equation then first solving [2] for t and then plugging into [1] and then +// solving the result for v(t) with the quadratic equation. +// +// So, moving forward and solving [1] for t via the quadratic equation, gives +// +// [3] t = - [ v0 +/- sqrt( v0^2 - 2ad(t) ) ] / a +// +// Substituting [3] into [2] then +// +// [4] v(t) = v0 - [ v0 +/- sqrt( v0^2 + 2ad(t) ) ] +// +// With some minor simplification and droping the (t) notation, we then have +// +// [5] v = -/+ sqrt( v0^2 + 2ad ) +// +// With equation [5], we then know the final speed v attained after accelerating +// from initial speed v0 over distance d. + +FORCE_INLINE FPTYPE final_speed(FPTYPE acceleration, FPTYPE initial_velocity, FPTYPE distance) { +#ifdef FIXED +// static int counts = 0; +#ifdef DEBUG_ZADVANCE + // DEBUG_TIMER_START; +#endif +#ifdef SIMULATOR + FPTYPE acceleration_original = acceleration; + FPTYPE distance_original = distance; + FPTYPE initial_velocity_original = initial_velocity; + float ftv = FPTOF(initial_velocity); + float fac = FPTOF(acceleration); + float fd = FPTOF(distance); + float fres = ftv * ftv + 2.0 * fac * fd; + if (fres <= 0.0) fres = 0.0; + else fres = sqrt(fres); +#endif + + // We wish to compute + // + // sum2 = initial_velocity * initial_velocity + 2 * acceleration * distance + // + // without having any overflows. We therefore judiciously divide + // both summands by 2^12. After computing sqrt(sum2), we will the + // multiply the result by 2^6 which is he square root of 2^12. + + // Note that when initial_velocity < 1, we lose velocity resolution. + // When acceleration or distance are < 1, we lose some resolution + // in them as well. We're here taking advantage of the fact that + // the acceleration is usually pretty large as are the velocities. + // Only the distances are sometimes small which is why we shift the + // distance the least. If this were to become a problem, we could + // shift the acceleration more and the distance even less. And, + // when the velocity is tiny relative to the product 2 * a * d, + // we really don't care as 2 * a * d will then dominate anyway. + // That is, losing resolution in the velocity is not, in practice, + // harmful since 2 * a * d will likely dominate in that case. + + initial_velocity >>= 12; + acceleration >>= 6; + distance >>= 5; // 2 * (distance >> 6) + FPTYPE sum2 = FPSQUARE(initial_velocity) + FPMULT2(distance, acceleration); + + // Now, comes the real speed up: use our fast 16 bit integer square + // root (in assembler nonetheles). To pave the way for this, we shift + // sum2 as much to the left as possible thereby maximizing the use of + // the "whole" or "integral" part of the fixed point number. We then + // take the square root of the integer part of sum2 which has been + // multiplied by 2^(2n) [n left shifts by 2]. After taking the square + // root, we correct this scaling by dividing the result by 2^n (which + // is the square root of 2^(2n) + + uint8_t n = 0; + while ((sum2 != 0) && (sum2 & 0xe0000000) == 0) { + sum2 <<= 2; + n++; + } + +#ifndef SIMULATOR + + // Generate the final result. We need to undo two sets of + // scalings: our original division by 2^12 which we rectify + // by multiplying by 2^6. But also we need to divide by 2^n + // so as to counter the 2^(2n) scaling we did. This means + // a net multiply by 2^(6-n). + + if (sum2 <= 0) return 0; + else if (n > 6) return ITOFP(isqrt1(FPTOI16(sum2))) >> (n - 6); + else return ITOFP(isqrt1(FPTOI16(sum2))) << (6 - n); + +#ifdef DEBUG_ZADVANCE +// Timing code +// FPTYPE result; +// if (sum2 <= 0) result = 0; +// else if (n > 6) result = ITOFP(isqrt1(FPTOI16(sum2))) >> (n - 6); +// else result = ITOFP(isqrt1(FPTOI16(sum2))) << (6 - n); +// DEBUG_TIMER_FINISH; +// zadvance2 += DEBUG_TIMER_TCTIMER_US; +// counts += 1; +// zadvance = zadvance2 / counts; +// return result; +#endif +#else +#define isqrt1(x) ((int32_t)sqrt((float)(x))) + FPTYPE result; + if (sum2 <= 0) result = 0; + else result = ITOFP(isqrt1(FPTOI16(sum2))); + if (n > 6) result >>= (n - 6); + else result <<= (6 - n); + if ((fres != 0.0) && ((fabsf(fres - FPTOF(result))/fres) > 0.05)) + { + char buf[1024]; + snprintf(buf, sizeof(buf), + "*** final_speed(%f, %f, %f): fixed result = %f; " + "float result = %f", + FPTOF(acceleration_original), + FPTOF(initial_velocity_original), + FPTOF(distance_original), + FPTOF(result), fres); + if (sblock) + { + if (sblock->message[0] != '\0') + strncat(sblock->message, "\n", sizeof(sblock->message)); + strncat(sblock->message, buf, sizeof(sblock->message)); + } + else + printf("%s\n", buf); + } + return result; +#endif // SIMULATOR +#else + // Just assume we're doing everything with floating point arithmetic + // and do not need to worry about overflows or underflows + FPTYPE v2 = FPSQUARE(initial_velocity) + 2.0 * acceleration * distance; + if (v2 <= 0) return 0; + else return FPSQRT(v2); +#endif // !FIXED +} + +#endif + // The kernel called by planner_recalculate() when scanning the plan from last to first entry. -void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *next) { +void planner_reverse_pass_kernel(block_t *current, block_t *next) { if(!current) { return; } if (next) { @@ -231,11 +970,24 @@ void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *n // If nominal length true, max junction speed is guaranteed to be reached. Only compute // for max allowable speed if block is decelerating and nominal length is false. - if ((!current->nominal_length_flag) && (current->max_entry_speed > next->entry_speed)) { + if ((!current->nominal_length_flag) && next->speed_changed && (current->max_entry_speed > next->entry_speed)) { +#ifndef FIXSIGN current->entry_speed = min( current->max_entry_speed, max_allowable_speed(-current->acceleration,next->entry_speed,current->millimeters)); +#else + // We want to know what speed to start at so that if we decelerate -- negative acceleration -- + // over distance current->millimeters, we end up at speed next->entry_speed + + // This call produces the same result as !defined(FIXSIGN) case +#ifdef SIMULATOR + sblock = current; +#endif + current->entry_speed = min( current->max_entry_speed, + initial_speed(-current->acceleration,next->entry_speed,current->millimeters)); +#endif } else { current->entry_speed = current->max_entry_speed; + current->speed_changed = true; } current->recalculate_flag = true; @@ -247,36 +999,51 @@ void planner_reverse_pass_kernel(block_t *previous, block_t *current, block_t *n // implements the reverse pass. void planner_reverse_pass() { uint8_t block_index = block_buffer_head; - if(((block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1)) > 3) { - block_index = (block_buffer_head - 3) & (BLOCK_BUFFER_SIZE - 1); - block_t *block[3] = { NULL, NULL, NULL }; - while(block_index != block_buffer_tail) { + block_t *block[2] = { NULL, NULL}; + + //Make a local copy of block_buffer_tail, because the interrupt can alter it + CRITICAL_SECTION_START; + unsigned char tail = block_buffer_tail; + CRITICAL_SECTION_END; + + while(block_index != tail) { block_index = prev_block_index(block_index); - block[2]= block[1]; block[1]= block[0]; block[0] = &block_buffer[block_index]; - planner_reverse_pass_kernel(block[0], block[1], block[2]); - } + planner_reverse_pass_kernel(block[0], block[1]); } } // The kernel called by planner_recalculate() when scanning the plan from first to last entry. -void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *next) { - if(!previous) { return; } +void planner_forward_pass_kernel(block_t *previous, block_t *current) { + if(!previous || !current->use_accel) { return; } // If the previous block is an acceleration block, but it is not long enough to complete the // full speed change within the block, we need to adjust the entry speed accordingly. Entry // speeds have already been reset, maximized, and reverse planned by reverse planner. // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. - if (!previous->nominal_length_flag) { - if (VNEQ(previous->entry_speed, current->entry_speed)) { - double entry_speed = min( current->entry_speed, + if (!previous->nominal_length_flag && previous->speed_changed) { + if (VLT(previous->entry_speed, current->entry_speed)) { +#ifndef FIXSIGN + FPTYPE entry_speed = min( current->entry_speed, max_allowable_speed(-previous->acceleration,previous->entry_speed,previous->millimeters) ); +#else + // We want to know what the terminal speed from the prior block would be if + // it accelerated over the entire block with starting speed prev->entry_speed + + // This call produces the same result as !defined(FIXSIGN) case +#ifdef SIMULATOR + sblock = previous; +#endif + FPTYPE entry_speed = min( current->entry_speed, + final_speed(previous->acceleration,previous->entry_speed,previous->millimeters) ); +#endif // Check for junction speed change if (VNEQ(current->entry_speed, entry_speed)) { current->entry_speed = entry_speed; current->recalculate_flag = true; + current->speed_changed = true; } } } @@ -286,23 +1053,21 @@ void planner_forward_pass_kernel(block_t *previous, block_t *current, block_t *n // implements the forward pass. void planner_forward_pass() { uint8_t block_index = block_buffer_tail; - block_t *block[3] = { NULL, NULL, NULL }; + block_t *block[2] = { NULL, NULL }; while(block_index != block_buffer_head) { block[0] = block[1]; - block[1] = block[2]; - block[2] = &block_buffer[block_index]; - planner_forward_pass_kernel(block[0],block[1],block[2]); + block[1] = &block_buffer[block_index]; + planner_forward_pass_kernel(block[0],block[1]); block_index = next_block_index(block_index); } - planner_forward_pass_kernel(block[1], block[2], NULL); } // Recalculates the trapezoid speed profiles for all blocks in the plan according to the // entry_factor for each junction. Must be called by planner_recalculate() after // updating the blocks. void planner_recalculate_trapezoids() { - int8_t block_index = block_buffer_tail; + uint8_t block_index = block_buffer_tail; block_t *current; block_t *next = NULL; @@ -313,17 +1078,20 @@ void planner_recalculate_trapezoids() { // Recalculate if current block entry or exit junction speed has changed. if (current->recalculate_flag || next->recalculate_flag) { // NOTE: Entry and exit factors always > 0 by all previous logic operations. - calculate_trapezoid_for_block(current, current->entry_speed/current->nominal_speed, - next->entry_speed/current->nominal_speed); + calculate_trapezoid_for_block(current, FPDIV(current->entry_speed,current->nominal_speed), + FPDIV(next->entry_speed,current->nominal_speed)); current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed } } block_index = next_block_index( block_index ); } - // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated. + // Last/newest block in buffer. Exit speed is set with minimumPlannerSpeed. Always recalculated. if(next != NULL) { - calculate_trapezoid_for_block(next, next->entry_speed/next->nominal_speed, - MINIMUM_PLANNER_SPEED/next->nominal_speed); + FPTYPE scaling = FPDIV(next->entry_speed,next->nominal_speed); + calculate_trapezoid_for_block(next, scaling, scaling); + // calculate_trapezoid_for_block(next, + // FPDIV(next->entry_speed,next->nominal_speed), + // FPDIV(minimumPlannerSpeed,next->nominal_speed)); next->recalculate_flag = false; } } @@ -352,37 +1120,51 @@ void planner_recalculate() { } -void plan_init(float extruderAdvanceK, float filamentDiameter, float axis_steps_per_unit_e) { +void plan_init(FPTYPE extruderAdvanceK, FPTYPE extruderAdvanceK2, bool zhold) { +#ifndef SIMULATOR stepperInterface = Motherboard::getBoard().getStepperAllInterfaces(); +#else + if ( (E_AXIS+1) != NUM_AXIS ) abort(); + if ( (X_AXIS >= NUM_AXIS) || + (Y_AXIS >= NUM_AXIS) || + (Z_AXIS >= NUM_AXIS) || + (E_AXIS >= NUM_AXIS) ) abort(); +#endif block_buffer_head = 0; block_buffer_tail = 0; memset(position, 0, sizeof(position)); // clear position - previous_speed[0] = 0.0; - previous_speed[1] = 0.0; - previous_speed[2] = 0.0; - previous_speed[3] = 0.0; - previous_nominal_speed = 0.0; - - extruder_advance_k = extruderAdvanceK; - extrution_area = 0.25 * filamentDiameter * filamentDiameter * 3.14159; - steps_per_cubic_mm_e = axis_steps_per_unit_e / extrution_area; + previous_inverse_millimeters = 0; + +#ifdef JKN_ADVANCE + extruder_advance_k = extruderAdvanceK; + extruder_advance_k2 = extruderAdvanceK2; +#endif + acceleration_zhold = zhold; + +#ifdef DEBUG_BLOCK_BY_MOVE_INDEX + current_move_index = 0; +#endif } -float junction_deviation = 0.1; // Add a new linear movement to the buffer. steps x, y and z is the absolute position in // steps. Microseconds specify how many microseconds the move should take to perform. To aid acceleration // calculation the caller must also provide the physical length of the line in millimeters. -void plan_buffer_line(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e, float feed_rate, const uint8_t &extruder) +// Returns true if command was executed, otherwise false of the command was dopped (due to use_accel = use_accel; + // Mark block as not busy (Not executed by the stepper interrupt) block->busy = false; +#ifndef CMD_SET_POSITION_CAUSES_DRAIN + memcpy(block->starting_position, position, sizeof(block->starting_position)); // starting_position[] = position[] +#endif + +#ifdef DEBUG_BLOCK_BY_MOVE_INDEX + block->move_index = ++ current_move_index; +#endif + +#ifdef SIMULATOR + // Track how many times this block is worked on by the planner + // Namely, how many times it is passed to calculate_trapezoid_for_block() + block->planned = 0; + block->message[0] = '\0'; + sblock = NULL; + if (prior_dropped[0] != '\0') + { + strncat(block->message, prior_dropped, sizeof(block->message)); + prior_dropped[0] = '\0'; + } +#endif + // Number of steps for each axis - block->steps_x = labs(target[X_AXIS]-position[X_AXIS]); - block->steps_y = labs(target[Y_AXIS]-position[Y_AXIS]); - block->steps_z = labs(target[Z_AXIS]-position[Z_AXIS]); - block->steps_e = labs(target[E_AXIS]-position[E_AXIS]); - block->step_event_count = max(block->steps_x, max(block->steps_y, max(block->steps_z, block->steps_e))); + block->steps[X_AXIS] = planner_steps[X_AXIS]; + block->steps[Y_AXIS] = planner_steps[Y_AXIS]; + block->steps[Z_AXIS] = planner_steps[Z_AXIS]; + block->steps[E_AXIS] = planner_steps[E_AXIS]; + block->step_event_count = planner_master_steps; + + //Figure out which is the master axis + for ( uint8_t i = 0; i < NUM_AXIS; i ++ ) { + if ( (uint32_t)labs(block->steps[i]) == block->step_event_count ) + block->dda_master_axis_index = i; + } // Bail if this is a zero-length block - if (block->step_event_count <=dropsegments) { return; }; + if (block->step_event_count <=dropsegments) +#ifndef SIMULATOR + return false; +#else + { + snprintf(prior_dropped, sizeof(prior_dropped), + "*** Segment prior to this one was dropped: x/y/z/e steps = " + "%d/%d/%d/%d, distance=%f", + planner_steps[X_AXIS], + planner_steps[Y_AXIS], + planner_steps[Z_AXIS], + planner_steps[E_AXIS], + FPTOF(planner_distance)); + return false; + } +#endif + + bool extruder_only_move = false; + if ( block->steps[X_AXIS] == 0 && block->steps[Y_AXIS] == 0 && block->steps[Z_AXIS] == 0 && block->steps[E_AXIS] != 0 ) + extruder_only_move = true; + + if (block->steps[E_AXIS] == 0) { + if(feed_ratefeed_rate = feed_rate; +#endif + // Compute direction bits for this block block->direction_bits = 0; if (target[X_AXIS] < position[X_AXIS]) { block->direction_bits |= (1<direction_bits |= (1<active_extruder = extruder; - + +#ifndef SIMULATOR //enable active axes - if(block->steps_x != 0) stepperInterface[X_AXIS].setEnabled(true); - if(block->steps_y != 0) stepperInterface[Y_AXIS].setEnabled(true); - if(block->steps_z != 0) stepperInterface[Z_AXIS].setEnabled(true); + if(block->steps[X_AXIS] != 0) stepperInterface[X_AXIS].setEnabled(true); + if(block->steps[Y_AXIS] != 0) stepperInterface[Y_AXIS].setEnabled(true); + if(block->steps[Z_AXIS] != 0) stepperInterface[Z_AXIS].setEnabled(true); // Enable all - if(block->steps_e != 0) { + if(block->steps[E_AXIS] != 0) { stepperInterface[E_AXIS].setEnabled(true); } +#endif - float delta_mm[4]; - delta_mm[X_AXIS] = (target[X_AXIS]-position[X_AXIS])/axis_steps_per_unit[X_AXIS]; - delta_mm[Y_AXIS] = (target[Y_AXIS]-position[Y_AXIS])/axis_steps_per_unit[Y_AXIS]; - delta_mm[Z_AXIS] = (target[Z_AXIS]-position[Z_AXIS])/axis_steps_per_unit[Z_AXIS]; - delta_mm[E_AXIS] = (target[E_AXIS]-position[E_AXIS])/axis_steps_per_unit[E_AXIS]; - if ( block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0 ) { - block->millimeters = abs(delta_mm[E_AXIS]); - } else { - block->millimeters = sqrt(ZSQUARE(delta_mm[X_AXIS]) + ZSQUARE(delta_mm[Y_AXIS]) + - ZSQUARE(delta_mm[Z_AXIS]) + ZSQUARE(delta_mm[E_AXIS])); + // slow down when de buffer starts to empty, rather than wait at the corner for a buffer refill + int moves_queued=(block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1); +#ifdef DEBUG_ZADVANCE + // if ( moves_queued < 2 ) zadvance += 1.0; +#endif + + //If we have an empty buffer, than anything "previous" should be wiped + if ( moves_queued == 0 ) { + #ifndef YET_ANOTHER_JERK + memset(previous_delta_mm, 0, sizeof(previous_delta_mm)); // clear position + #else + memset(prev_speed, 0, sizeof(prev_speed)); + #endif + previous_inverse_millimeters = 0; } - float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides - - // Calculate speed in mm/second for each axis. No divide by zero due to previous checks. - float inverse_second = feed_rate * inverse_millimeters; - - block->nominal_speed = block->millimeters * inverse_second; // (mm/sec) Always > 0 - block->nominal_rate = ceil(block->step_event_count * inverse_second); // (step/sec) Always > 0 +// SLOWDOWN +if ( slowdown_limit ) { + //Renable slowdown if we have half filled up the buffer + if (( disable_slowdown ) && ( moves_queued >= slowdown_limit )) disable_slowdown = false; - - - if (block->steps_e == 0) { - if(feed_rate 1) + feed_rate = FPDIV( FPMULT2(feed_rate, ITOFP(moves_queued)), ITOFP((int32_t)slowdown_limit)); +} +// END SLOWDOWN + if ( extruder_only_move ) { + block->millimeters = FPABS(delta_mm[E_AXIS]); + } else { + block->millimeters = planner_distance; - // slow down when de buffer starts to empty, rather than wait at the corner for a buffer refill - int moves_queued=(block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1); -#ifdef SLOWDOWN - if(moves_queued < (BLOCK_BUFFER_SIZE * 0.5) && moves_queued > 1) feed_rate = feed_rate*moves_queued / (BLOCK_BUFFER_SIZE * 0.5); + //If we have one item in the buffer, then control it's minimum time with minimumSegmentTime + if ((moves_queued < 1 ) && (minimumSegmentTime > 0) && ( block->millimeters > 0 ) && + ( feed_rate > 0 ) && (( FPDIV(block->millimeters, feed_rate) ) < minimumSegmentTime)) { + feed_rate = FPDIV(block->millimeters, minimumSegmentTime); + } + } +#ifdef FIXED + FPTYPE inverse_millimeters = FPDIV(KCONSTANT_1, block->millimeters); // Inverse millimeters to remove multiple divides +#else + FPTYPE inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides +#endif + // Calculate speed in mm/second for each axis. No divide by zero due to previous checks. + FPTYPE inverse_second = FPMULT2(feed_rate, inverse_millimeters); + +#ifdef NO_CEIL_FLOOR + block->nominal_speed = feed_rate; // (mm/sec) Always > 0 +#ifdef FIXED + block->nominal_rate = (uint32_t)FPTOI((KCONSTANT_0_5 + FPMULT2(ITOFP((int32_t)block->step_event_count), inverse_second))); // (step/sec) Always > 0 +#else + block->nominal_rate = (uint32_t)(0.5 + block->step_event_count * inverse_second); // (step/sec) Always > 0 #endif +#else + block->nominal_speed = FPMULT2(block->millimeters, inverse_second); // (mm/sec) Always > 0 + block->nominal_rate = (uint32_t)FPTOI(FPCEIL(FPMULT2(ITOFP((int32_t)block->step_event_count), inverse_second))); // (step/sec) Always > 0 +#endif + // Calculate speed in mm/sec for each axis - float current_speed[4]; - for(int i=0; i < 4; i++) { - current_speed[i] = delta_mm[i] * inverse_second; + FPTYPE current_speed[NUM_AXIS]; + for(unsigned char i=0; i < NUM_AXIS; i++) { + current_speed[i] = FPMULT2(delta_mm[i], inverse_second); } // Limit speed per axis - float speed_factor = 1.0; //factor <=1 do decrease speed - for(int i=0; i < 4; i++) { - if(abs(current_speed[i]) > max_feedrate[i]) - speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i])); +#ifdef FIXED + FPTYPE speed_factor = KCONSTANT_1; //factor <=1 do decrease speed +#else + FPTYPE speed_factor = 1.0; //factor <=1 do decrease speed +#endif + if ( use_accel ) { + if ( !extruder_only_move ) { + for(unsigned char i=0; i < NUM_AXIS; i++) + if(FPABS(current_speed[i]) > max_feedrate[i]) + speed_factor = min(speed_factor, FPDIV(max_feedrate[i], FPABS(current_speed[i]))); + } + else if(FPABS(current_speed[E_AXIS]) > extruder_only_max_feedrate) + speed_factor = FPDIV(extruder_only_max_feedrate, FPABS(current_speed[E_AXIS])); + } + else { + for(unsigned char i=0; i < NUM_AXIS; i++) + if(FPABS(current_speed[i]) > max_speed_change[i]) + speed_factor = min(speed_factor, FPDIV(max_speed_change[i], FPABS(current_speed[i]))); } // Correct the speed +#ifdef FIXED + if( speed_factor < KCONSTANT_1 ) { +#else if( speed_factor < 1.0) { - for(int i=0; i < 4; i++) { - if(abs(current_speed[i]) > max_feedrate[i]) - speed_factor = min(speed_factor, max_feedrate[i] / abs(current_speed[i])); - } - for(unsigned char i=0; i < 4; i++) { - current_speed[i] *= speed_factor; +#endif + for(unsigned char i=0; i < NUM_AXIS; i++) { + current_speed[i] = FPMULT2(current_speed[i], speed_factor); } - block->nominal_speed *= speed_factor; - block->nominal_rate *= speed_factor; + block->nominal_speed = FPMULT2(block->nominal_speed, speed_factor); + block->nominal_rate = (uint32_t)FPTOI(FPMULT2(ITOFP((int32_t)block->nominal_rate), speed_factor)); } // Compute and limit the acceleration rate for the trapezoid generator. - float steps_per_mm = block->step_event_count/block->millimeters; - if(block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0) { - block->acceleration_st = ceil(p_retract_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 + FPTYPE steps_per_mm = FPDIV(ITOFP((int32_t)block->step_event_count), block->millimeters); + if ( extruder_only_move ) { +#ifdef NO_CEIL_FLOOR +#ifdef FIXED + //Assumptions made, due to the high value of acceleration_st / p_retract acceleration, dropped + //ceil and floating point multiply + block->acceleration_st = (uint32_t)(FPTOI(p_retract_acceleration) * FPTOI(steps_per_mm)); // convert to: acceleration steps/sec^2 +#else + block->acceleration_st = (uint32_t)(0.5 + p_retract_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 +#endif +#else + block->acceleration_st = (uint32_t)(FPTOI(FPCEIL(FPMULT2(p_retract_acceleration, steps_per_mm)))); // convert to: acceleration steps/sec^2 +#endif } else { - block->acceleration_st = ceil(p_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 +#ifdef NO_CEIL_FLOOR +#ifdef FIXED + //Assumptions made, due to the high value of acceleration_st / p_retract acceleration, dropped + //ceil and floating point multiply + block->acceleration_st = (uint32_t)(FPTOI(p_acceleration) * FPTOI(steps_per_mm)); // convert to: acceleration steps/sec^2 +#else + block->acceleration_st = (uint32_t)(0.5 + p_acceleration * steps_per_mm); // convert to: acceleration steps/sec^2 +#endif +#else + block->acceleration_st = (uint32_t)(FPTOI(FPCEIL(FPMULT2(p_acceleration, steps_per_mm)))); // convert to: acceleration steps/sec^2 +#endif // Limit acceleration per axis - if(((float)block->acceleration_st * (float)block->steps_x / (float)block->step_event_count) > axis_steps_per_sqr_second[X_AXIS]) - block->acceleration_st = axis_steps_per_sqr_second[X_AXIS]; - if(((float)block->acceleration_st * (float)block->steps_y / (float)block->step_event_count) > axis_steps_per_sqr_second[Y_AXIS]) - block->acceleration_st = axis_steps_per_sqr_second[Y_AXIS]; - if(((float)block->acceleration_st * (float)block->steps_e / (float)block->step_event_count) > axis_steps_per_sqr_second[E_AXIS]) - block->acceleration_st = axis_steps_per_sqr_second[E_AXIS]; - if(((float)block->acceleration_st * (float)block->steps_z / (float)block->step_event_count ) > axis_steps_per_sqr_second[Z_AXIS]) - block->acceleration_st = axis_steps_per_sqr_second[Z_AXIS]; - } - block->acceleration = block->acceleration_st / steps_per_mm; - block->acceleration_rate = (int32_t)((float)block->acceleration_st * 8.388608); + for (uint8_t i = 0; i <= E_AXIS; i++) + if((block->steps[i] != 0) && + ((block->acceleration_st * (uint32_t)block->steps[i]) > (axis_steps_per_sqr_second[i] * block->step_event_count))) + block->acceleration_st = axis_steps_per_sqr_second[i]; + } + block->acceleration = FPDIV(ITOFP((int32_t)block->acceleration_st), steps_per_mm); + + // The value 8.388608 derives from the timer frequency used for + // st_interrupt(). That interrupt is driven by a timer counter which + // ticks at a frequency of 2 MHz. To convert counter values to seconds + // the counter value needs to be divided by 2000000. So that we + // can do integer arithmetic (rather than floating point), we first + // multiply the acceleration by the counter value and THEN divide the + // result by 2000000. However, the divide can be done by a shift and + // it turns out that it is convenient to use >> 24 which is a divide + // by approximately 16777216. That's too large by about 8.388608. + // Therefore, we pre-scale the acceleration here by 8.388608 + + //This can potentially overflow in fixed point, due to a large block->acceleration_st, + //so we don't use fixed point for this calculation +#ifdef FIXED + block->acceleration_rate = (int32_t)(((int64_t)block->acceleration_st * 137439) >> 14); +#else + block->acceleration_rate = (int32_t)((FPTYPE)block->acceleration_st * 8.388608); +#endif -#if 0 // Use old jerk for now - // Compute path unit vector - double unit_vec[3]; +#ifdef YET_ANOTHER_JERK + FPTYPE scaling = KCONSTANT_1; + bool docopy = true; + if ( moves_queued == 0 ) + { + vmax_junction = minimumPlannerSpeed; + scaling = FPDIV(vmax_junction, block->nominal_speed); + } + else if ( (block->nominal_speed <= smallest_max_speed_change) || (!use_accel) ) + { + vmax_junction = block->nominal_speed; + // scaling remains KCONSTANT_1 + } + else + { + FPTYPE delta_v; + for (uint8_t i = 0; i < NUM_AXIS; i++) + { + delta_v = FPABS(current_speed[i] - prev_speed[i]); + if ( delta_v > max_speed_change[i] ) + { + // We wish to moderate max_entry_speed such that delta_v + // remains <= max_speed_change. Moreover, any moderation we + // apply to the speed along this axis, we need to uniformly + // apply to all axes and, more importantly, to nominal_speed. + // As such, we need to determine a scaling factor, s. + + // There's two approaches we can take without altering the + // prior block. + // + // Approach 1 -- approximate + // + // s1 = scaling factor for approach 1 + // s1 * abs(current_speed - prev_speed) <= max_speed_change + // + // and thus + // + // [1] s1 = max_speed_change / abs(current_speed - prev_speed) + // + // Note that if max_speed_change > 0 and [1] is only applied when + // + // [2] abs(current_speed - prev_speed) <= max_speed_change + // + // then s1 always obeys + // + // [3] 0 < s1 < 1. + // + // This approach is "approximate" in that we will only scale + // current_speed (the present block). We will not scale the + // prior block. But, the planner will strive to scale the + // final speed of the prior block to match this scaled entry + // speed. + // + // Approach 2 + // + // s2 = scaling factor for approach 2 + // abs(s2 * current_speed - prev_speed) <= max_speed_change + // + // which leads to + // + // [4a] s2 = abs( (prev_speed - max_speed_change) / current_speed ) + // WHEN (current_speed - prev_speed) < 0 + // + // [4b] s2 = abs( (prev_speed + max_speed_change) / current_speed ) + // WHEN (current_speed - prev_speed) > 0 + // + // Unfortunately, s2 has the range + // + // [5] 0 <= s2 <= infinity. + // + // The difficulty with using [4a] and [4b] is that they can lead to + // very small scalings, s2. For example, when the numerator is zero + // or close to zero. Additionally, they lead to an infinite scaling + // when the current_speed is zero. + // + // In theory, we could pick and choose + // + // s = max(s1, min(s2, 1)) + // + // However, the benefit is slight and the additional code complexity + // (code space), and compute time doesn't make it worthwhile. As such + // we stick to using the first approach, [2]. + + // Avoid using min(x,y) as it may generate duplicate + // computations if it is a macro + FPTYPE s = FPDIV(max_speed_change[i], delta_v); + if ( s < scaling ) scaling = s; + } + } + if (scaling != KCONSTANT_1) + { + vmax_junction = FPMULT2(block->nominal_speed, scaling); + for (uint8_t i = 0; i < NUM_AXIS; i++) + prev_speed[i] = FPMULT2(current_speed[i], scaling); + docopy = false; + } + else + // scaling remains KCONSTANT_1 + vmax_junction = block->nominal_speed; + } + if ( docopy ) memcpy(prev_speed, current_speed, sizeof(prev_speed)); - unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters; - unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters; - unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inverse_millimeters; - - // Compute maximum allowable entry speed at junction by centripetal acceleration approximation. - // Let a circle be tangent to both previous and current path line segments, where the junction - // deviation is defined as the distance from the junction to the closest edge of the circle, - // colinear with the circle center. The circular segment joining the two paths represents the - // path of centripetal acceleration. Solve for max velocity based on max acceleration about the - // radius of the circle, defined indirectly by junction deviation. This may be also viewed as - // path width or max_jerk in the previous grbl version. This approach does not actually deviate - // from path, but used as a robust way to compute cornering speeds, as it takes into account the - // nonlinearities of both the junction angle and junction velocity. - double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed - - // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. - if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) { - // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) - // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. - double cos_theta = - previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] - - previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] - - previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; - - // Skip and use default max junction speed for 0 degree acute junction. - if (cos_theta < 0.95) { - vmax_junction = min(previous_nominal_speed,block->nominal_speed); - // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. - if (cos_theta > -0.95) { - // Compute maximum junction velocity based on maximum acceleration and junction deviation - double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. - vmax_junction = min(vmax_junction, - sqrt(block->acceleration * junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) ); - } - } +#else +// START COSINE_JERK2 + +#ifdef DEBUG_ZADVANCE + // DEBUG_TIMER_START; +#endif + + if ( moves_queued == 0 ) + { + vmax_junction = minimumPlannerSpeed; + } + else if ( (block->nominal_speed <= max_speed_change[0]) || (!use_accel) ) + { + vmax_junction = block->nominal_speed; } + // The cosine of the angle A between two vectors V1 and V2 is given by + // + // cos(A) = dot-product(V1, V2) / [ norm(V1) norm(V2) ] + // + // When a vector V is a unit vector (normalized), then norm(V) = 1 + // Thus, we have + // + // cos(A) = dot-product(V1, V2) + // + // Further, recall that if -90 < A < 90, then cos(A) > 0 + // if abs(A) == 90, then cos(A) = 0 + // if abs(A) > 90, then cos(A) < 0 + // + // Thus, the sign of cos(A) tells us if the two vectors are + // at less than 90 degrees, 90 degrees, or more than 90 degrees + // This is of interest as it tells us how we're turning between + // each segment. + else if (block->steps[X_AXIS] != 0 || block->steps[Y_AXIS] != 0) + { + cosine = FPMULT3((FPMULT2(previous_delta_mm[X_AXIS], delta_mm[X_AXIS]) + + FPMULT2(previous_delta_mm[Y_AXIS], delta_mm[Y_AXIS]) + + FPMULT2(previous_delta_mm[Z_AXIS], delta_mm[Z_AXIS]) + + FPMULT2(previous_delta_mm[E_AXIS], delta_mm[E_AXIS])), + inverse_millimeters, previous_inverse_millimeters); + + // Convert the angle to an integer "turn" in the range -10 <= t <= 10 + // Note that cos( 0) = +1.000 ==> turn = +10 + // cos(+/ -45) = +0.707 ==> turn = 7 + // cos(+/ -90) = +0.000 ==> turn = 0 + // cos(+/-180) = -1.000 ==> turn = -10 + +#ifdef FIXED + if ( cosine <= 0 ) +#else + //The following code is a floating point trick to compare for < + //without having to do a floating point op. + //http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm + //Saves upto 150uS + //Replaces: + // if (cosine <= 0.0) + if ( *(int32_t *)&cosine < *(int32_t *)&compareLessThanEqualZero ) #endif - // Start with a safe speed - float vmax_junction = max_xy_jerk/2; - if(abs(current_speed[Z_AXIS]) > max_z_jerk/2) - vmax_junction = max_z_jerk/2; - vmax_junction = min(vmax_junction, block->nominal_speed); + { + // Turn exceeds 90 degrees: apply serious breaking at the junction + // + // 1.0 + cos(+/- 90) = 1.0 for 90 degrees + // 1.0 + cos(+/- 180) = 0.0 for 180 degrees + vmax_junction = max_speed_change[0]; + } + else + { + // Turn < 90 degrees + // Apply breaking down to min_junction_speed based on the sharpness of the angle + vmax_junction = FPMULT2(block->nominal_speed - max_speed_change[0], cosine) + max_speed_change[0]; +#ifdef FIXED + vmax_junction = max(vmax_junction, max_speed_change[0]); +#else + //The following code is a floating point trick to compare for < + //without having to do a floating point op. + //http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm + //Saves upto 100uS + //Replaces: + // vmax_junction = max(vmax_junction, max_speed_change); + if ( *(int32_t *)&vmax_junction < *(int32_t *)&max_speed_change ) + vmax_junction = max_speed_change[0]; +#endif + } + } + else + { + // No motion in the X or Y plane + if (block->steps[E_AXIS] == 0) + // Z only move (or no move) + vmax_junction = max_feedrate[Z_AXIS]; + else if (block->steps[Z_AXIS] == 0) + // E only move (or no move) + vmax_junction = extruder_only_max_feedrate; + else + // E and Z move + vmax_junction = min(max_feedrate[E_AXIS], max_feedrate[Z_AXIS]); + + vmax_junction = min(block->nominal_speed, vmax_junction); + } - if ((moves_queued > 1) && (previous_nominal_speed > 0.0)) { - float jerk_squared = ZSQUARE(current_speed[X_AXIS]-previous_speed[X_AXIS]) + ZSQUARE(current_speed[Y_AXIS]-previous_speed[Y_AXIS]); + // Save for next pass + previous_inverse_millimeters = inverse_millimeters; + memcpy(previous_delta_mm, delta_mm, sizeof(previous_delta_mm)); // previous_delta_mm[] = delta_mm[] - if((previous_speed[X_AXIS] != 0.0) || (previous_speed[Y_AXIS] != 0.0)) { - vmax_junction = block->nominal_speed; - } - if (jerk_squared > max_xy_jerk_squared) { - vmax_junction *= sqrt((max_xy_jerk_squared/jerk_squared)); - } - if(abs(current_speed[Z_AXIS] - previous_speed[Z_AXIS]) > max_z_jerk) { - vmax_junction *= (max_z_jerk/abs(current_speed[Z_AXIS] - previous_speed[Z_AXIS])); - } - } +#ifdef DEBUG_ZADVANCE + // DEBUG_TIMER_FINISH; + // zadvance2 = DEBUG_TIMER_TCTIMER_US; +#endif + +//END COSINE_JERK2 + +#endif + +#ifdef DEBUG_ZADVANCE + // if ( block->steps[Z_AXIS] != 0 ) + // zadvance2 = FPTOF(vmax_junction); +#endif + + // Initialize block entry speed. Compute based on deceleration to user-defined minimumPlannerSpeed. +#ifndef FIXSIGN + FPTYPE v_allowable = max_allowable_speed(-block->acceleration,minimumPlannerSpeed,block->millimeters); block->max_entry_speed = vmax_junction; - - // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. - double v_allowable = max_allowable_speed(-block->acceleration,MINIMUM_PLANNER_SPEED,block->millimeters); - block->entry_speed = min(vmax_junction, v_allowable); + if (use_accel) block->entry_speed = min(vmax_junction, v_allowable); + else block->entry_speed = vmax_junction; +#else + // We want our speed to be AT LEAST fast enough such that we hit minimumPlannerSpeed by the block's end + // It's okay, however, if we go even faster so take the max of vmax_junction and v_allowable + + // This call produces the same result as the !FIXSIGN case + // It's the max. speed we can achieve if we accelerate the entire length of the block + // starting with an initial speed of minimumPlannerSpeed +#ifdef SIMULATOR + sblock = block; +#endif + FPTYPE v_allowable = final_speed(block->acceleration,minimumPlannerSpeed,block->millimeters); + + // And this will typically produce a larger value than the !defined(FIXSIGN) case + if (vmax_junction < minimumPlannerSpeed) { + block->entry_speed = minimumPlannerSpeed; + block->max_entry_speed = minimumPlannerSpeed; + } else { + block->entry_speed = vmax_junction; + block->max_entry_speed = vmax_junction; + } +#endif // Initialize planner efficiency flags // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. @@ -581,59 +1680,70 @@ void plan_buffer_line(const int32_t &x, const int32_t &y, const int32_t &z, cons if (block->nominal_speed <= v_allowable) { block->nominal_length_flag = true; } else { block->nominal_length_flag = false; } block->recalculate_flag = true; // Always calculate trapezoid for new block - - // Update previous path unit_vector and nominal speed - memcpy(previous_speed, current_speed, sizeof(previous_speed)); // previous_speed[] = current_speed[] - previous_nominal_speed = block->nominal_speed; - - #ifdef ADVANCE - // Calculate advance rate - if((block->steps_e == 0) || (block->steps_x == 0 && block->steps_y == 0 && block->steps_z == 0) || ( extruder_advance_k == 0.0 )) { - block->advance_rate = 0; - block->advance = 0; - } - else { - int32_t acc_dist = estimate_acceleration_distance(0, block->nominal_rate, block->acceleration_st); - float advance = (steps_per_cubic_mm_e * extruder_advance_k) * - (current_speed[E_AXIS] * current_speed[E_AXIS] * extrution_area * extrution_area)*256; - block->advance = advance; - if(acc_dist == 0) { - block->advance_rate = 0; - } - else { - block->advance_rate = advance / (float)acc_dist; - } - } - #endif // ADVANCE - - - + block->speed_changed = false; + +#ifdef JKN_ADVANCE + block->advance_pressure_relax = 0; + if((block->steps[E_AXIS] == 0) || ( extruder_only_move ) || (( extruder_advance_k == 0 ) && ( extruder_advance_k2 == 0)) || ( !use_accel )) { + block->use_advance_lead = false; + block->advance_lead_entry = 0; + block->advance_lead_exit = 0; + block->advance_lead_prime = 0; + block->advance_lead_deprime = 0; + block->advance_pressure_relax = 0; + } else { + block->use_advance_lead = true; + } +#endif - calculate_trapezoid_for_block(block, block->entry_speed/block->nominal_speed, - MINIMUM_PLANNER_SPEED/block->nominal_speed); +#ifndef YET_ANOTHER_JERK + FPTYPE scaling = FPDIV(block->entry_speed,block->nominal_speed); +#endif + calculate_trapezoid_for_block(block, scaling, scaling); + +#ifdef JKN_ADVANCE + //if ( block->advance_lead_entry < 0 ) zadvance = block->advance_lead_entry; + //if ( block->advance_lead_exit < 0 ) zadvance = block->advance_lead_exit; + //if ( block->advance_lead_prime < 0 ) zadvance = block->advance_lead_prime; + //if ( block->advance_lead_deprime < 0 ) zadvance = block->advance_lead_deprime; + //zadvance2 = block->advance_pressure_relax; +#endif // Move buffer head block_buffer_head = next_buffer_head; // Update position - memcpy(position, target, sizeof(target)); // position[] = target[] + memcpy(position, target, sizeof(position)); // position[] = target[] + + +#ifdef DEBUG_ZADVANCE + #ifdef FIXED + // zadvance = FPTOF(roundk(FTOFP(2.8934), 3)); //0 = 7 1,2 = 3.0 8 = 2.895 + // zadvance2 = FPTOF(roundk(FTOFP(2.3846), 3)); //0 = 6 1,2 = 2.5 8 = 2.383 + #endif +#endif planner_recalculate(); - st_wake_up(); + + return true; } void plan_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e) { +#ifndef CMD_SET_POSITION_CAUSES_DRAIN + CRITICAL_SECTION_START; // Fill variables used by the stepper in a critical section +#endif position[X_AXIS] = x; position[Y_AXIS] = y; position[Z_AXIS] = z; position[E_AXIS] = e; +#ifndef CMD_SET_POSITION_CAUSES_DRAIN + CRITICAL_SECTION_END; // Fill variables used by the stepper in a critical section +#endif + +#ifdef CMD_SET_POSITION_CAUSES_DRAIN st_set_position(position[X_AXIS], position[Y_AXIS], position[Z_AXIS], position[E_AXIS]); - previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest. - previous_speed[0] = 0.0; - previous_speed[1] = 0.0; - previous_speed[2] = 0.0; - previous_speed[3] = 0.0; +#endif } void plan_set_e_position(const int32_t &e) @@ -645,6 +1755,46 @@ void plan_set_e_position(const int32_t &e) uint8_t movesplanned() { return (block_buffer_head-block_buffer_tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1); +} + + + +//Figure out the acceleration stats by scanning through the command pipeline + +void accelStatsGet(float *minSpeed, float *avgSpeed, float *maxSpeed) { + block_t *block; + int32_t count = 0; + uint8_t block_index = block_buffer_tail; + + FPTYPE smax = 0, savg = 0; +#ifdef FIXED + FPTYPE smin = KCONSTANT_1000; +#else + FPTYPE smin = 1000.0; +#endif + + while(block_index != block_buffer_head) { + block = &block_buffer[block_index]; + + smin = min(smin, block->entry_speed); + smax = max(smax, block->nominal_speed); + savg += block->nominal_speed; + + block_index = next_block_index(block_index); + count ++; + } + + if ( count ) { + //We have stats + *minSpeed = FPTOF(smin); + *maxSpeed = FPTOF(smax); + *avgSpeed = FPTOF(FPDIV(savg, ITOFP(count))); + } else { + //We have no stats + *minSpeed = 0.0; + *maxSpeed = 0.0; + *avgSpeed = 0.0; + } } #endif diff --git a/firmware/src/shared/StepperAccelPlanner.hh b/firmware/src/shared/StepperAccelPlanner.hh index 92978fd..3aaec78 100644 --- a/firmware/src/shared/StepperAccelPlanner.hh +++ b/firmware/src/shared/StepperAccelPlanner.hh @@ -25,23 +25,128 @@ #define STEPPERACCELPLANNER_HH #include +#include "avrfix.h" +#include "Configuration.hh" -// extruder advance constant (s2/mm3) -// -// advance (steps) = STEPS_PER_CUBIC_MM_E * EXTUDER_ADVANCE_K * cubic mm per second ^ 2 -// -// hooke's law says: force = k * distance -// bernoulli's priniciple says: v ^ 2 / 2 + g . h + pressure / density = constant -// so: v ^ 2 is proportional to number of steps we advance the extruder -#define ADVANCE +#ifndef NOFIXED +#define FIXED +#else +#ifdef FIXED +#undef FIXED +#endif +#endif + +#ifdef FIXED + #define FPTYPE _Accum + + #define FPTOI(x) ktoli(x) //FPTYPE -> int32_t + #define FPTOI16(x) ktoi(x) //FPTYPE -> int16_t + #define ITOFP(x) itok(x) //int32_t -> FPTYPE + #define FTOFP(x) ftok(x) //float -> FPTYPE + #define FPTOF(x) ktof(x) //FPTYPE -> float + + #define FPSQUARE(x) mulk(x,x) + #define FPMULT2(x,y) mulk(x,y) + #define FPMULT3(x,y,a) mulk(mulk(x,y),a) + #define FPMULT4(x,y,a,b) mulk(mulk(mulk(x,y),a),b) + #define FPDIV(x,y) divk(x,y) + #define FPSQRT(x) sqrtk(x) + #define FPABS(x) absk(x) + #define FPCEIL(x) roundk(x + KCONSTANT_0_5, 3) + #define FPFLOOR(x) roundk(x - KCONSTANT_0_5, 3) + + //Various constants we need, we preconvert these to fixed point to save time later + #define KCONSTANT_MINUS_0_95 -62259 //ftok(-0.95) + #define KCONSTANT_0_001 65 //ftok(0.001) + #define KCONSTANT_0_1 6553 //ftok(0.1) + #define KCONSTANT_0_25 16384 //ftok(0.25) + #define KCONSTANT_0_5 32768 //ftok(0.5) + #define KCONSTANT_0_95 62259 //ftok(0.95) + #define KCONSTANT_1 65536 //ftok(1.0) + #define KCONSTANT_3 196608 //ftok(3.0) + #define KCONSTANT_8_388608 549755 //ftok(8.388608) + #define KCONSTANT_10 655360 //ftok(10.0) + #define KCONSTANT_30 1966080 //ftok(30.0) + #define KCONSTANT_100 6553600 //ftok(100.0) + #define KCONSTANT_256 16777216 //ftok(256.0) + #define KCONSTANT_1000 65536000 //ftok(1000.0) + #define KCONSTANT_1000000_LSR_16 1000000 //ftok(1000000.0) >> 16 + +#else + #define FPTYPE float + + #define FPTOI(x) (int32_t)(x) //FPTYPE -> int32_t + #define FPTOI16(x) (int16_t)(x) //FPTYPE -> int16_t + #define ITOFP(x) (float)(x) //int32_t -> FPTYPE + #define FTOFP(x) (x) //Do nothing cos we're already float + #define FPTOF(x) (x) //Do nothing cos we're already float + + #define FPSQUARE(x) ((x) * (x)) + #define FPMULT2(x,y) ((x) * (y)) + #define FPMULT3(x,y,a) ((x) * (y) * (a)) + #define FPMULT4(x,y,a,b) ((x) * (y) * (a) * (b)) + #define FPDIV(x,y) ((x) / (y)) + #define FPSQRT(x) sqrt(x) + #define FPABS(x) abs(x) + #define FPCEIL(x) ceil(x) + #define FPFLOOR(x) floor(x) + +#endif + +//If defined, support for recording the current move within the block is compiled in +//#define DEBUG_BLOCK_BY_MOVE_INDEX + +//When commented out, HOST_CMD_SET_POSITION and HOST_CMD_SET_POSITION_EXT are +//handled asynchronously. +//When not commented out, when these commands are encountered, the buffer is completely drained before executing +//them. +//#define CMD_SET_POSITION_CAUSES_DRAIN + +//Oversample the dda to provide less jitter. +//To switch off oversampling, comment out +//2 is the number of bits, as in a bit shift. So << 2 = multiply by 4 +//= 4 times oversampling +//Obviously because of this oversampling is always a power of 2. +//Don't make it too large, as it will kill performance and can overflow int32_t +//#define OVERSAMPLED_DDA 2 + +//Keep the dda "phase" between line segments +//If false, each new line segment is started as if it was a new line segment, i.e. no prior history +//If true, each new line segment takes into account the phase of the last segment +#define DDA_KEEP_PHASE false + +#define JKN_ADVANCE + +//Disabled for now, probably does work but needs a non-symmetrical prime/deprime +//If defined, enables advance_lead_prime / advance_lead_deprime +//#define JKN_ADVANCE_LEAD_DE_PRIME + +//Use acceleration instead of delta velocity for advance_lead_entry / advance_lead_exit +//Doesn't work with JKN_ADVANCE_LEAD_DE_PRIME at this point +//#define JKN_ADVANCE_LEAD_ACCEL -#define SLOWDOWN +//Drop ceil/floor calculations. Making this available as a #define so we can test timing later +#define NO_CEIL_FLOOR + +// If defined then uses final_speed() and initial_speed() instead of max_allowable_speed() +#define FIXSIGN #define NUM_AXIS 4 // The axis order in all axis related arrays is X, Y, Z, E // The number of linear motions that can be in the plan at any give time. // THE BLOCK_BUFFER_SIZE NEEDS TO BE A POWER OF 2, i.g. 8,16,32 because shifts and ors are used to do the ringbuffering. -#define BLOCK_BUFFER_SIZE 32 // maximize block buffer +#ifdef SMALL_4K_RAM + #define BLOCK_BUFFER_SIZE 8 // maximize block buffer +#else + #define BLOCK_BUFFER_SIZE 16 // maximize block buffer +#endif + +// When SAVE_SPACE is defined, the code doesn't take some optimizations which +// which lead to additional program space usage. +//#define SAVE_SPACE + +// Use yet another jerk calc +#define YET_ANOTHER_JERK #define FORCE_INLINE __attribute__((always_inline)) inline @@ -51,27 +156,32 @@ // the source g-code and may never actually be reached if acceleration management is active. typedef struct { // Fields used by the bresenham algorithm for tracing the line - int32_t steps_x, steps_y, steps_z, steps_e; // Step count along each axis + int32_t steps[NUM_AXIS]; // Step count along each axis uint32_t step_event_count; // The number of step events required to complete this block +#ifndef CMD_SET_POSITION_CAUSES_DRAIN + int32_t starting_position[NUM_AXIS]; +#endif int32_t accelerate_until; // The index of the step event on which to stop acceleration int32_t decelerate_after; // The index of the step event on which to start decelerating int32_t acceleration_rate; // The acceleration rate used for acceleration calculation unsigned char direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) unsigned char active_extruder; // Selects the active extruder - #ifdef ADVANCE - int32_t advance_rate; - volatile int32_t initial_advance; - volatile int32_t final_advance; - float advance; + #ifdef JKN_ADVANCE + bool use_advance_lead; + int32_t advance_lead_entry; + int32_t advance_lead_exit; + int32_t advance_pressure_relax; //Decel phase only + int32_t advance_lead_prime; + int32_t advance_lead_deprime; #endif // Fields used by the motion planner to manage acceleration - // float speed_x, speed_y, speed_z, speed_e; // Nominal mm/minute for each axis - float nominal_speed; // The nominal speed for this block in mm/min - float entry_speed; // Entry speed at previous-current junction in mm/min - float max_entry_speed; // Maximum allowable junction entry speed in mm/min - float millimeters; // The total travel of this block in mm - float acceleration; // acceleration mm/sec^2 + // FPTYPE speed_x, speed_y, speed_z, speed_e; // Nominal mm/minute for each axis + FPTYPE nominal_speed; // The nominal speed for this block in mm/min + FPTYPE entry_speed; // Entry speed at previous-current junction in mm/min + FPTYPE max_entry_speed; // Maximum allowable junction entry speed in mm/min + FPTYPE millimeters; // The total travel of this block in mm + FPTYPE acceleration; // acceleration mm/sec^2 unsigned char recalculate_flag; // Planner flag to recalculate trapezoids on entry junction unsigned char nominal_length_flag; // Planner flag for nominal speed always reached @@ -80,15 +190,29 @@ typedef struct { uint32_t initial_rate; // The jerk-adjusted step rate at start of block uint32_t final_rate; // The minimal rate at exit uint32_t acceleration_st; // acceleration steps/sec^2 + char use_accel; // Use acceleration when true + char speed_changed; // Entry speed has changed volatile char busy; + +#ifdef SIMULATOR + FPTYPE feed_rate; // Original feed rate before being modified for nomimal_speed + int planned; // Count of the number of times the block was passed to caclulate_trapezoid_for_block() + char message[1024]; +#endif + +#ifdef DEBUG_BLOCK_BY_MOVE_INDEX + uint32_t move_index; +#endif + + uint8_t dda_master_axis_index; } block_t; // Initialize the motion plan subsystem -void plan_init(float extruderAdvanceK, float filamentDiameter, float axis_steps_per_unit_e); +void plan_init(FPTYPE extruderAdvanceK, FPTYPE extruderAdvanceK2, bool zhold); // Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in // steps. Feed rate specifies the speed of the motion. -void plan_buffer_line(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e, float feed_rate, const uint8_t &extruder); +bool plan_buffer_line(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e, FPTYPE feed_rate, const uint8_t &extruder, bool use_accel=true); // Set position. Used for G92 instructions. void plan_set_position(const int32_t &x, const int32_t &y, const int32_t &z, const int32_t &e); @@ -97,21 +221,34 @@ void plan_set_e_position(const int32_t &e); uint8_t movesplanned(); //return the nr of buffered moves extern uint32_t minsegmenttime; -extern float max_feedrate[4]; // set the max speeds -extern float axis_steps_per_unit[4]; -extern uint32_t max_acceleration_units_per_sq_second[4]; // Use M201 to override by software -extern float minimumfeedrate; -extern float p_acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX -extern float p_retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX -extern float max_xy_jerk; //speed than can be stopped at once, if i understand correctly. -extern float max_xy_jerk_squared; // max_xy_jerk * max_xy_jerk -extern float max_z_jerk; -extern float mintravelfeedrate; +extern FPTYPE max_feedrate[NUM_AXIS]; // set the max speeds +extern uint32_t max_acceleration_units_per_sq_second[NUM_AXIS]; // Use M201 to override by software +extern FPTYPE minimumfeedrate; +extern FPTYPE p_acceleration; // Normal acceleration mm/s^2 THIS IS THE DEFAULT ACCELERATION for all moves. M204 SXXXX +extern FPTYPE p_retract_acceleration; // mm/s^2 filament pull-pack and push-forward while standing still in the other axis M204 TXXXX +extern FPTYPE max_speed_change[NUM_AXIS]; //The speed between junctions in the planner, reduces blobbing +extern FPTYPE smallest_max_speed_change; + +extern FPTYPE mintravelfeedrate; +extern FPTYPE minimumSegmentTime; extern uint32_t axis_steps_per_sqr_second[NUM_AXIS]; +extern bool acceleration_zhold; +extern FPTYPE delta_mm[NUM_AXIS]; +extern FPTYPE planner_distance; +extern FPTYPE minimumPlannerSpeed; +extern uint32_t planner_master_steps; +extern int32_t planner_steps[NUM_AXIS]; +extern FPTYPE extruder_only_max_feedrate; +extern int slowdown_limit; + +#ifndef CMD_SET_POSITION_CAUSES_DRAIN + extern int32_t position[NUM_AXIS]; +#endif extern block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions extern volatile unsigned char block_buffer_head; // Index of the next block to be pushed extern volatile unsigned char block_buffer_tail; +extern float axis_steps_per_unit[NUM_AXIS]; // Called when the current block is no longer needed. Discards the block and makes the memory // availible for new blocks. FORCE_INLINE void plan_discard_current_block() @@ -141,4 +278,13 @@ FORCE_INLINE bool blocks_queued() else return true; } + +extern void accelStatsGet(float *minSpeed, float *avgSpeed, float *maxSpeed); + +#ifndef SIMULATOR +#define SIMULATOR_RECORD(x...) +#else +#include "SimulatorRecord.hh" +#endif + #endif diff --git a/firmware/src/shared/StepperAccelSpeedTableBuild.c b/firmware/src/shared/StepperAccelSpeedTableBuild.c new file mode 100644 index 0000000..f044932 --- /dev/null +++ b/firmware/src/shared/StepperAccelSpeedTableBuild.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +#define TIMER_FREQ 2000000 + +main() +{ + int i,j; + int array[256][2]; + printf("#ifndef SPEED_LOOKUPTABLE_H\n"); + printf("#define SPEED_LOOKUPTABLE_H\n"); + printf("\n"); + printf("#include \n"); + printf("\n"); + printf("uint16_t speed_lookuptable_fast[256][2] PROGMEM = {\n"); + for(i=0;i<256;i++){ + array[i][0] = TIMER_FREQ / ((i*256)+32); + } + for(i=0;i<255;i++){ + array[i][1] = array[i][0] - array[i+1][0]; + } + array[255][1] = array[254][1]; + for(i=0;i<32;i++) { + for(j=0;j<8;j++) { + printf("{ %d, %d}, ", array[i*8+j][0], array[i*8+j][1]); + } + printf("\n"); + } + printf("};\n"); + printf("uint16_t speed_lookuptable_slow[256][2] PROGMEM = {\n"); + for(i=0;i<256;i++){ + array[i][0] = TIMER_FREQ / ((i*8)+32); + } + for(i=0;i<255;i++){ + array[i][1] = array[i][0] - array[i+1][0]; + } + array[255][1] = array[254][1]; + for(i=0;i<32;i++) { + for(j=0;j<8;j++) { + printf("{ %d, %d}, ", array[i*8+j][0], array[i*8+j][1]); + } + printf("\n"); + } + printf("};\n"); + printf("\n"); + printf("#endif\n"); +} diff --git a/firmware/src/shared/StepperAxis.cc b/firmware/src/shared/StepperAxis.cc index 115b3fd..55be906 100644 --- a/firmware/src/shared/StepperAxis.cc +++ b/firmware/src/shared/StepperAxis.cc @@ -102,7 +102,7 @@ bool StepperAxis::checkEndstop(const bool isHoming) { void StepperAxis::doInterrupt(const int32_t intervals) { counter += delta; - if (counter >= 0) { + if (counter > 0) { interface->setDirection(direction); counter -= intervals; bool hit_endstop = checkEndstop(false); @@ -121,7 +121,7 @@ void StepperAxis::doInterrupt(const int32_t intervals) { bool StepperAxis::doHoming(const int32_t intervals) { if (delta == 0) return false; counter += delta; - if (counter >= 0) { + if (counter > 0) { interface->setDirection(direction); counter -= intervals; bool hit_endstop = checkEndstop(true); diff --git a/firmware/src/shared/StepperInterface.cc b/firmware/src/shared/StepperInterface.cc index 2b508fe..4475546 100644 --- a/firmware/src/shared/StepperInterface.cc +++ b/firmware/src/shared/StepperInterface.cc @@ -18,6 +18,7 @@ #include "StepperInterface.hh" #include "Eeprom.hh" #include "EepromMap.hh" +#include "EepromDefaults.hh" #include "Configuration.hh" StepperInterface::StepperInterface(const Pin& dir, @@ -75,7 +76,8 @@ void StepperInterface::init(uint8_t idx) { enable_pin.setValue(true); enable_pin.setDirection(true); // get inversion characteristics - uint8_t axes_invert = eeprom::getEeprom8(eeprom_base, 1<<1); +#ifdef EEPROM_DEFAULT_AXIS_INVERSION + uint8_t axes_invert = eeprom::getEeprom8(eeprom_base, EEPROM_DEFAULT_AXIS_INVERSION); uint8_t endstops_invert = eeprom::getEeprom8(eeprom_base + 1, 0); bool endstops_present = (endstops_invert & (1<<7)) != 0; @@ -92,4 +94,5 @@ void StepperInterface::init(uint8_t idx) { min_pin.setDirection(false); min_pin.setValue(invert_endstops); } +#endif } diff --git a/firmware/src/shared/UART.cc b/firmware/src/shared/UART.cc index a53f9c2..19d4960 100644 --- a/firmware/src/shared/UART.cc +++ b/firmware/src/shared/UART.cc @@ -61,7 +61,7 @@ volatile uint8_t loopback_bytes = 0; UCSR0C = _BV(UCSZ01)|_BV(UCSZ00); \ } -#elif defined (__AVR_ATmega644P__) +#elif defined (__AVR_ATmega644P__) || defined (OVERRIDE_UART_TO_38400_BAUD) #define UBRR_VALUE 25 #define UBRRA_VALUE 0 diff --git a/firmware/src/shared/avrfix/avrfix.c b/firmware/src/shared/avrfix/avrfix.c new file mode 100644 index 0000000..69009c5 --- /dev/null +++ b/firmware/src/shared/avrfix/avrfix.c @@ -0,0 +1,989 @@ +/**************************************************************** + * * + * __ ____ _________ * + * /_ \\\ \/ /| \______ * + * // \\\ /|| D /_ /. * + * // \\\_ /.|| \ ___/. * + * /___/\___\\__/. |__|\__\__.___ ___ * + * .... ....... ...|| _/_\ \////. * + * || |.| |\ ///. * + * |__|.|_|/// \ * + * .... ./__/\__\ * + * ........ * + * Fixed Point Library * + * according to * + * ISO/IEC DTR 18037 * + * * + * Version 1.0.1 * + * Maximilan Rosenblattl, Andreas Wolf 2007-02-07 * + ****************************************************************/ +#ifndef TEST_ON_PC +#include +#include +#include +#include +#endif +#include "avrfix.h" +#include "avrfix_config.h" + +#if BYTE_ORDER == BIG_ENDIAN +typedef struct { + uint16_t ll; + uint8_t lh; + int8_t h; +} lAccum_container; +#else +typedef struct { + int8_t h; + uint8_t lh; + uint16_t ll; +} lAccum_container; +#endif + +#define us(x) ((uint16_t)(x)) +#define ss(x) ((int16_t)(x)) +#define ul(x) ((uint32_t)(x)) +#define sl(x) ((int32_t)(x)) + +extern void cordicck(_Accum* x, _Accum* y, _Accum* z, uint8_t iterations, uint8_t mode); +extern void cordichk(_Accum* x, _Accum* y, _Accum* z, uint8_t iterations, uint8_t mode); +extern void cordiccsk(_sAccum* x, _sAccum* y, _sAccum* z, uint8_t mode); +extern void cordichsk(_sAccum* x, _sAccum* y, _sAccum* z, uint8_t mode); + +#ifdef SMULSKD +_sAccum smulskD(_sAccum x, _sAccum y) +{ + return ss(RSHIFT_static(sl(x)*sl(y), SACCUM_FBIT)); +} +#endif +#ifdef SMULSKS +_sAccum smulskS(_sAccum x, _sAccum y) +{ + int32_t mul = RSHIFT_static(sl(x)*sl(y), SACCUM_FBIT); + if(mul >= 0) { + if((mul & 0xFFFF8000) != 0) + return SACCUM_MAX; + } else { + if((mul & 0xFFFF8000) != 0xFFFF8000) + return SACCUM_MIN; + } + return sl(mul); +} +#endif +#ifdef MULKD +_Accum mulkD(_Accum x, _Accum y) +{ +#if BYTE_ORDER == BIG_ENDIAN +# define LO 0 +# define HI 1 +#else +# define LO 1 +# define HI 0 +#endif + uint16_t xs[2]; + uint16_t ys[2]; + int8_t positive = ((x < 0 && y < 0) || (y > 0 && x > 0)) ? 1 : 0; + y = absk(y); + *((_Accum*)xs) = absk(x); + *((_Accum*)ys) = y; + x = sl(xs[HI])*y + sl(xs[LO])*sl(ys[HI]); + *((_Accum*)xs) = ul(xs[LO])*ul(ys[LO]); + if(positive) + return x + us(xs[HI]); + else + return -(x + us(xs[HI])); +#undef HI +#undef LO +} +#endif +#ifdef MULKS +_Accum mulkS(_Accum x, _Accum y) +{ +#if BYTE_ORDER == BIG_ENDIAN +# define LO 0 +# define HI 1 +#else +# define LO 1 +# define HI 0 +#endif + uint16_t xs[2]; + uint16_t ys[2]; + uint32_t mul; + int8_t positive = ((x < 0 && y < 0) || (y > 0 && x > 0)) ? 1 : 0; + *((_Accum*)xs) = absk(x); + *((_Accum*)ys) = absk(y); + mul = ul(xs[HI]) * ul(ys[HI]); + if(mul > 32767) + return (positive ? ACCUM_MAX : ACCUM_MIN); + mul = LSHIFT_static(mul, ACCUM_FBIT) + + ul(xs[HI])*ul(ys[LO]) + + ul(xs[LO])*ul(ys[HI]) + + RSHIFT_static(ul(xs[LO]*ys[LO]), ACCUM_FBIT); + if(mul & 0x80000000) + return (positive ? ACCUM_MAX : ACCUM_MIN); + return (positive ? (int32_t)mul : -(int32_t)mul); +#undef HI +#undef LO +} +#endif +#ifdef LMULLKD +_lAccum lmullkD(_lAccum x, _lAccum y) +{ + lAccum_container *xc, *yc; + xc = (lAccum_container*)&x; + yc = (lAccum_container*)&y; + return sl(xc->h)*y + sl(yc->h)*(x&0x00FFFFFF) + + ((ul(xc->lh)*ul(yc->lh))*256) + + RSHIFT_static((ul(xc->lh)*ul(yc->ll) + ul(xc->ll)*ul(yc->lh)), 8) + + (RSHIFT_static((ul(xc->lh)*ul(yc->ll) + ul(xc->ll)*ul(yc->lh)), 7)&1) + + RSHIFT_static((ul(xc->ll)*ul(yc->ll)), 24); +} +#endif +#ifdef LMULLKS +_lAccum lmullkS(_lAccum x, _lAccum y) +{ + lAccum_container xc, yc; + uint32_t mul; + int8_t positive = ((x < 0 && y < 0) || (y > 0 && x > 0)) ? 1 : 0; + x = labslk(x); + y = labslk(y); + *((_lAccum*)&xc) = x; + *((_lAccum*)&yc) = y; + mul = xc.h * yc.h; + x &= 0x00FFFFFF; + y &= 0x00FFFFFF; + if(mul > 127) + return (positive ? LACCUM_MAX : LACCUM_MIN); + mul = LSHIFT_static(mul, LACCUM_FBIT) + ul(xc.h)*y + ul(yc.h)*x + + + (ul(xc.lh)*ul(yc.lh)*256) + + RSHIFT_static((ul(xc.lh)*ul(yc.ll) + ul(xc.ll)*ul(yc.lh)), 8) + + (RSHIFT_static((ul(xc.lh)*ul(yc.ll) + ul(xc.ll)*ul(yc.lh)), 7)&1) + + RSHIFT_static((ul(xc.ll)*ul(yc.ll)), 24); + if(mul & 0x80000000) + return (positive ? ACCUM_MAX : ACCUM_MIN); + return (positive ? (int32_t)mul : -(int32_t)mul); +} +#endif +#ifdef SDIVSKD +_sAccum sdivskD(_sAccum x, _sAccum y) +{ + return ss((sl(x) << SACCUM_FBIT) / y); +} +#endif +#ifdef SDIVSKS +_sAccum sdivskS(_sAccum x, _sAccum y) +{ + int32_t div; + if(y == 0) + return (x < 0 ? SACCUM_MIN : SACCUM_MAX); + div = (sl(x) << SACCUM_FBIT) / y; + if(div >= 0) { + if((div & 0xFFFF8000) != 0) + return SACCUM_MAX; + } else { + if((div & 0xFFFF8000) != 0xFFFF8000) + return SACCUM_MIN; + } + return ss(div); +} +#endif +#ifdef DIVKD +/* if y = 0, divkD will enter an endless loop */ +_Accum divkD(_Accum x, _Accum y) { + if ( y == 0 ) return ACCUM_INFINITY; + _Accum result; + int i,j=0; + int8_t sign = ((x < 0 && y < 0) || (x > 0 && y > 0)) ? 1 : 0; + x = absk(x); + y = absk(y); + /* Align x leftmost to get maximum precision */ + + for (i=0 ; i= ACCUM_MAX / 2) break; + x = LSHIFT_static(x, 1); + } + while((y & 1) == 0) { + y = RSHIFT_static(y, 1); + j++; + } + result = x/y; + + /* Correct value by shift left */ + /* Check amount and direction of shifts */ + i = (ACCUM_FBIT - i) - j; + if(i > 0) + result = LSHIFT_dynamic(result, i); + else if(i < 0) { + /* shift right except for 1 bit, wich will be used for rounding */ + result = RSHIFT_dynamic(result, (-i) - 1); + /* determine if round is necessary */ + result = RSHIFT_static(result, 1) + (result & 1); + } + return (sign ? result : -result); +} +#endif +#ifdef DIVKS +_Accum divkS(_Accum x, _Accum y) { + _Accum result; + int i,j=0; + int8_t sign = ((x < 0 && y < 0) || (y > 0 && x > 0)) ? 1 : 0; + if(y == 0) + return (x < 0 ? ACCUM_MIN : ACCUM_MAX); + x = absk(x); + y = absk(y); + + for (i=0 ; i= ACCUM_MAX / 2) break; + x = LSHIFT_static(x, 1); + } + + while((y & 1) == 0) { + y = RSHIFT_static(y, 1); + j++; + } + + result = x/y; + + /* Correct value by shift left */ + /* Check amount and direction of shifts */ + i = (ACCUM_FBIT - i) - j; + if(i > 0) + for(;i>0;i--) { + if((result & 0x40000000) != 0) { + return sign ? ACCUM_MAX : ACCUM_MIN; + } + result = LSHIFT_static(result, 1); + } + else if(i < 0) { + /* shift right except for 1 bit, wich will be used for rounding */ + result = RSHIFT_dynamic(result, (-i) - 1); + /* round */ + result = RSHIFT_static(result, 1) + (result & 1); + } + return (sign ? result : -result); +} +#endif +#ifdef LDIVLKD +/* if y = 0, ldivlkD will enter an endless loop */ +_lAccum ldivlkD(_lAccum x, _lAccum y) { + _lAccum result; + int i,j=0; + int8_t sign = ((x < 0 && y < 0) || (x > 0 && y > 0)) ? 1 : 0; + x = labslk(x); + y = labslk(y); + /* Align x leftmost to get maximum precision */ + + for (i=0 ; i= LACCUM_MAX / 2) break; + x = LSHIFT_static(x, 1); + } + while((y & 1) == 0) { + y = RSHIFT_static(y, 1); + j++; + } + result = x/y; + + /* Correct value by shift left */ + /* Check amount and direction of shifts */ + i = (LACCUM_FBIT - i) - j; + if(i > 0) + result = LSHIFT_dynamic(result, i); + else if(i < 0) { + /* shift right except for 1 bit, wich will be used for rounding */ + result = RSHIFT_dynamic(result, (-i) - 1); + /* determine if round is necessary */ + result = RSHIFT_static(result, 1) + (result & 1); + } + return (sign ? result : -result); +} +#endif +#ifdef LDIVLKS +_lAccum ldivlkS(_lAccum x, _lAccum y) { + _lAccum result; + int i,j=0; + int8_t sign = ((x < 0 && y < 0) || (y > 0 && x > 0)) ? 1 : 0; + if(y == 0) + return (x < 0 ? LACCUM_MIN : LACCUM_MAX); + x = labslk(x); + y = labslk(y); + + for (i=0 ; i= LACCUM_MAX / 2) break; + x = LSHIFT_static(x, 1); + } + + while((y & 1) == 0) { + y = RSHIFT_static(y, 1); + j++; + } + + result = x/y; + + /* Correct value by shift left */ + /* Check amount and direction of shifts */ + i = (LACCUM_FBIT - i) - j; + if(i > 0) + for(;i>0;i--) { + if((result & 0x40000000) != 0) { + return sign ? LACCUM_MAX : LACCUM_MIN; + } + result = LSHIFT_static(result, 1); + } + else if(i < 0) { + /* shift right except for 1 bit, wich will be used for rounding */ + result = RSHIFT_dynamic(result, (-i) - 1); + /* round */ + result = RSHIFT_static(result, 1) + (result & 1); + } + return (sign ? result : -result); +} +#endif +#ifdef SINCOSK +_Accum sincosk(_Accum angle, _Accum* cosp) +{ + _Accum x; + _Accum y = 0; + uint8_t correctionCount = 0; + uint8_t quadrant = 1; + if(cosp == NULL) + cosp = &x; + + /* move large values into [0,2 PI] */ +#define MAX_CORRECTION_COUNT 1 + while(angle >= PIlk) { /* PIlk = PIk * 2^8 */ + angle -= PIlk; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle++; + } else { + correctionCount++; + } + } + correctionCount = 0; + while(angle < 0) { + angle += PIlk; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle--; + } else { + correctionCount++; + } + } +#undef MAX_CORRECTION_COUNT + + /* move small values into [0,2 PI] */ +#define MAX_CORRECTION_COUNT 5 + while(angle >= 2*PIk + 1) { + angle -= 2*PIk + 1; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle++; + } else { + correctionCount++; + } + } + if(correctionCount > 0) { + angle++; + } + correctionCount = 0; + while(angle < 0) { + angle += 2*PIk + 1; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle--; + } else { + correctionCount++; + } + } + if(correctionCount > 0) { + angle--; + } +#undef MAX_CORRECTION_COUNT + + if(angle > PIk) { + angle = angle - PIk; + quadrant += 2; + } + if(angle > (PIk/2 + 1)) { + angle = PIk - angle + 1; + quadrant += 1; + } + if(angle == 0) { + *cosp = (quadrant == 2 || quadrant == 3 ? -itok(1) : itok(1)); + return 0; + } + *cosp = CORDICC_GAIN; + angle = LSHIFT_static(angle, 8); + cordicck(cosp, &y, &angle, 17, 0); + (*cosp) = RSHIFT_static(*cosp, 8); + y = RSHIFT_static(y, 8); + switch(quadrant) { + case 2: { + (*cosp) = -(*cosp); + } break; + case 3: { + y = -y; + (*cosp) = -(*cosp); + } break; + case 4: { + y = -y; + } break; + default:; + } + return y; +} +#endif +#ifdef LSINCOSLK +_lAccum lsincoslk(_lAccum angle, _lAccum* cosp) +{ + _lAccum x; + _lAccum y = 0; + uint8_t correctionCount; + uint8_t quadrant = 1; + if(cosp == NULL) + cosp = &x; + + /* move values into [0, 2 PI] */ +#define MAX_CORRECTION_COUNT 1 + correctionCount = 0; + while(angle >= 2*PIlk) { + angle -= 2*PIlk; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle++; + } else { + correctionCount++; + } + } + correctionCount = 0; + while(angle < 0) { + angle += 2*PIlk; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle--; + } else { + correctionCount++; + } + } +#undef MAX_CORRECTION_COUNT + + if(angle > PIlk) { + angle = angle - PIlk; + quadrant += 2; + } + if(angle > (PIlk/2)) { + angle = PIlk - angle; + quadrant += 1; + } + if(angle == 0) { + *cosp = (quadrant == 2 || quadrant == 3 ? -itolk(1) : itolk(1)); + return 0; + } + *cosp = CORDICC_GAIN; + cordicck(cosp, &y, &angle, 24, 0); + switch(quadrant) { + case 2: { + (*cosp) = -(*cosp); + } break; + case 3: { + y = -y; + (*cosp) = -(*cosp); + } break; + case 4: { + y = -y; + } break; + default:; + } + return y; +} +#endif +#ifdef LSINCOSK +_lAccum lsincosk(_Accum angle, _lAccum* cosp) +{ + uint8_t correctionCount = 0; + /* move large values into [0,2 PI] */ +#define MAX_CORRECTION_COUNT 1 + while(angle >= PIlk) { /* PIlk = PIk * 2^8 */ + angle -= PIlk; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle++; + } else { + correctionCount++; + } + } + correctionCount = 0; + while(angle < 0) { + angle += PIlk; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle--; + } else { + correctionCount++; + } + } +#undef MAX_CORRECTION_COUNT + + /* move small values into [0,2 PI] */ +#define MAX_CORRECTION_COUNT 5 + while(angle >= 2*PIk + 1) { + angle -= 2*PIk + 1; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle++; + } else { + correctionCount++; + } + } + if(correctionCount > 0) { + angle++; + } + correctionCount = 0; + while(angle < 0) { + angle += 2*PIk + 1; + if(correctionCount == MAX_CORRECTION_COUNT) { + correctionCount = 0; + angle--; + } else { + correctionCount++; + } + } + if(correctionCount > 0) { + angle--; + } +#undef MAX_CORRECTION_COUNT + return lsincoslk(LSHIFT_static(angle, (LACCUM_FBIT - ACCUM_FBIT)), cosp); +} +#endif +#ifdef ROUNDSKD +/* + * Difference from ISO/IEC DTR 18037: + * using an uint8_t as second parameter according to + * microcontroller register size and maximum possible value + */ +_sAccum roundskD(_sAccum f, uint8_t n) +{ + n = SACCUM_FBIT - n; + if(f >= 0) { + return (f & (0xFFFF << n)) + ((f & (1 << (n-1))) << 1); + } else { + return (f & (0xFFFF << n)) - ((f & (1 << (n-1))) << 1); + } +} +#endif +#ifdef ROUNDKD +/* + * Difference from ISO/IEC DTR 18037: + * using an uint8_t as second parameter according to + * microcontroller register size and maximum possible value + */ +_Accum roundkD(_Accum f, uint8_t n) +{ + n = ACCUM_FBIT - n; + if(f >= 0) { + return (f & (0xFFFFFFFF << n)) + ((f & (1 << (n-1))) << 1); + } else { + return (f & (0xFFFFFFFF << n)) - ((f & (1 << (n-1))) << 1); + } +} +#endif +#ifdef ROUNDSKS +/* + * Difference from ISO/IEC DTR 18037: + * using an uint8_t as second parameter according to + * microcontroller register size and maximum possible value + */ +_sAccum roundskS(_sAccum f, uint8_t n) +{ + if(n > SACCUM_FBIT) { + return 0; + } + return roundskD(f, n); +} +#endif +#ifdef ROUNDKS +/* + * Difference from ISO/IEC DTR 18037: + * using an uint8_t as second parameter according to + * microcontroller register size and maximum possible value + */ +_Accum roundkS(_Accum f, uint8_t n) +{ + if(n > ACCUM_FBIT) { + return 0; + } + return roundkD(f, n); +} +#endif +#ifdef ROUNDLKD +/* + * Difference from ISO/IEC DTR 18037: + * using an uint8_t as second parameter according to + * microcontroller register size and maximum possible value + */ +_lAccum roundlkD(_lAccum f, uint8_t n) +{ + n = LACCUM_FBIT - n; + if(f >= 0) { + return (f & (0xFFFFFFFF << n)) + ((f & (1 << (n-1))) << 1); + } else { + return (f & (0xFFFFFFFF << n)) - ((f & (1 << (n-1))) << 1); + } +} +#endif +#ifdef ROUNDLKS +/* + * Difference from ISO/IEC DTR 18037: + * using an uint8_t as second parameter according to + * microcontroller register size and maximum possible value + */ +_Accum roundlkS(_lAccum f, uint8_t n) +{ + if(n > LACCUM_FBIT) { + return 0; + } + return roundlkD(f, n); +} +#endif +#ifdef COUNTLSSK +/* + * Difference from ISO/IEC DTR 18037: + * using an uint8_t as second parameter according to + * microcontroller register size and maximum possible value + */ +uint8_t countlssk(_sAccum f) +{ + int8_t i; + uint8_t *pf = ((uint8_t*)&f) + 2; + for(i = 0; i < 15; i++) { + if((*pf & 0x40) != 0) + break; + f = LSHIFT_static(f, 1); + } + return i; +} +#endif +#ifdef COUNTLSK +/* + * Difference from ISO/IEC DTR 18037: + * using an uint8_t as second parameter according to + * microcontroller register size and maximum possible value + */ +uint8_t countlsk(_Accum f) +{ + int8_t i; + uint8_t *pf = ((uint8_t*)&f) + 3; + for(i = 0; i < 31; i++) { + if((*pf & 0x40) != 0) + break; + f = LSHIFT_static(f, 1); + } + return i; +} +#endif +#ifdef TANKD +_Accum tankD(_Accum angle) +{ + _Accum sin, cos; + sin = sincosk(angle, &cos); + if(absk(cos) <= 2) + return (sin < 0 ? ACCUM_MIN : ACCUM_MAX); + return divkD(sin, cos); +} +#endif +#ifdef TANKS +_Accum tankS(_Accum angle) +{ + _Accum sin, cos; + sin = sincosk(angle, &cos); + if(absk(cos) <= 2) + return (sin < 0 ? ACCUM_MIN : ACCUM_MAX); + return divkS(sin, cos); +} +#endif +#ifdef LTANLKD +_lAccum ltanlkD(_lAccum angle) +{ + _lAccum sin, cos; + sin = lsincoslk(angle, &cos); + if(absk(cos) <= 2) + return (sin < 0 ? LACCUM_MIN : LACCUM_MAX); + return ldivlkD(sin, cos); +} +#endif +#ifdef LTANLKS +_lAccum ltanlkS(_lAccum angle) +{ + _lAccum sin, cos; + sin = lsincoslk(angle, &cos); + if(absk(cos) <= 2) + return (sin < 0 ? LACCUM_MIN : LACCUM_MAX); + return ldivlkS(sin, cos); +} +#endif +#ifdef LTANKD +_lAccum ltankD(_Accum angle) +{ + _lAccum sin, cos; + sin = lsincosk(angle, &cos); + return ldivlkD(sin, cos); +} +#endif +#ifdef LTANKS +_lAccum ltankS(_Accum angle) +{ + _lAccum sin, cos; + sin = lsincosk(angle, &cos); + if(absk(cos) <= 2) + return (sin < 0 ? LACCUM_MIN : LACCUM_MAX); + return ldivlkS(sin, cos); +} +#endif +#ifdef ATAN2K +_Accum atan2kInternal(_Accum x, _Accum y) +{ + _Accum z = 0; + uint8_t i = 0; + uint8_t *px = ((uint8_t*)&x) + 3, *py = ((uint8_t*)&y) + 3; + for(;!(*px & 0x60) && !(*py & 0x60) && i < 8;i++) { + x = LSHIFT_static(x, 1); + y = LSHIFT_static(y, 1); + } + if(i > 0) { + cordicck(&x, &y, &z, 16, 1); + return RSHIFT_static(z, 8); + } else { + return PIk/2 - divkD(x, y) - 1; + } +} + +_Accum atan2k(_Accum x, _Accum y) +{ + uint8_t signX, signY; + if(y == 0) + return 0; + signY = (y < 0 ? 0 : 1); + if(x == 0) + return (signY ? ACCUM_MAX : ACCUM_MIN); + signX = (x < 0 ? 0 : 1); + x = atan2kInternal(absk(x), absk(y)); + if(signY) { + if(signX) { + return x; + } else { + return x + PIk/2 + 1; + } + } else { + if(signX) { + return -x; + } else { + return -x - PIk/2 - 1; + } + } +} +#endif +#ifdef LATAN2LK +_lAccum latan2lk(_lAccum x, _lAccum y) +{ + uint8_t signX, signY; + _Accum z = 0; + uint8_t *px = ((uint8_t*)&x) + 3, *py = ((uint8_t*)&y) + 3; + if(y == 0) + return 0; + signY = (y < 0 ? 0 : 1); + if(x == 0) + return (signY ? ACCUM_MAX : ACCUM_MIN); + signX = (x < 0 ? 0 : 1); + if(!signX) + x = -x; + if(!signY) + y = -y; + if((*px & 0x40) || (*py & 0x40)) { + x = RSHIFT_static(x, 1); + y = RSHIFT_static(y, 1); + } + cordicck(&x, &y, &z, 24, 1); + if(signY) { + if(signX) { + return z; + } else { + return z+PIlk/2; + } + } else { + if(signX) { + return -z; + } else { + return -z-PIlk/2; + } + } +} +#endif +#ifdef CORDICCK +/* + * calculates the circular CORDIC method in both modes + * mode = 0: + * Calculates sine and cosine with input z and output x and y. To be exact + * x has to be CORDIC_GAIN instead of itok(1) and y has to be 0. + * + * mode = 1: + * Calculates the arctangent of y/x with output z. No correction has to be + * done here. + * + * iterations is the fractal bit count (16 for _Accum, 24 for _lAccum) + * and now the only variable, the execution time depends on. + */ +void cordicck(_Accum* px, _Accum* py, _Accum* pz, uint8_t iterations, uint8_t mode) +{ + const uint32_t arctan[25] = {13176795, 7778716, 4110060, 2086331, 1047214, 524117, 262123, 131069, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1}; + register uint8_t i; + _Accum x, y, z, xH; + x = *px; + y = *py; + z = *pz; + for (i = 0; i <= iterations; i++) { + xH = x; + if((mode && y <= 0) || (!mode && z >= 0)) { + x -= RSHIFT_dynamic(y, i); + y += RSHIFT_dynamic(xH, i); + z -= arctan[i]; + } + else { + x += RSHIFT_dynamic(y, i); + y -= RSHIFT_dynamic(xH, i); + z += arctan[i]; + } + } + *px = x; + *py = y; + *pz = z; +} +#endif +#ifdef CORDICHK +/* + * calculates the hyperbolic CORDIC method in both modes + * mode = 0: + * Calculates hyperbolic sine and cosine with input z and output x and y. + * To be exact x has to be CORDICH_GAIN instead of itok(1) and y has to be 0. + * This mode is never used in this library because of target limitations. + * + * mode = 1: + * Calculates the hyperbolic arctangent of y/x with output z. No correction + * has to be done here. + * + * iterations is the fractal bit count (16 for _Accum, 24 for _lAccum) + */ +void cordichk(_Accum* px, _Accum* py, _Accum* pz, uint8_t iterations, uint8_t mode) +{ + const uint32_t arctanh[24] = {9215828, 4285116, 2108178, 1049945, 524459, 262165, 131075, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1}; + register uint8_t i, j; + _Accum x, y, z, xH; + x = *px; + y = *py; + z = *pz; + for (i = 1; i <= iterations; i++) { + for(j = 0; j < 2; j++) {/*repeat iterations 4, 13, 40, ... 3k+1*/ + xH = x; + if((mode && y <= 0) || (!mode && z >= 0)) { + x += RSHIFT_dynamic(y, i); + y += RSHIFT_dynamic(xH, i); + z -= arctanh[i-1]; + } + else { + x -= RSHIFT_dynamic(y, i); + y -= RSHIFT_dynamic(xH, i); + z += arctanh[i-1]; + } + if(i != 4 && i != 13) + break; + } + } + *px = x; + *py = y; + *pz = z; +} +#endif +#ifdef SQRT +_Accum sqrtk_uncorrected(_Accum a, int8_t pow2, uint8_t cordic_steps) +{ + _Accum x, y, z; + if(a <= 0) + return 0; + /* The cordich method works only within [0.03, 2] + * for other values the following identity is used: + * + * sqrt(2^n * a) = sqrt(a) * sqrt(2^n) = sqrt(a) * 2^(n/2) + * + * Here, the interval [0.06, 1] is taken, because the + * number of shifts may be odd and the correction shift + * may be outside the original interval in that case. + */ + for(; a > 16777216; pow2++) + a = RSHIFT_static(a, 1); + for(; a < 1006592; pow2--) + a = LSHIFT_static(a, 1); + /* pow2 has to be even */ + if(pow2 > 0 && pow2 & 1) { + pow2--; + a = LSHIFT_static(a, 1); + } else if(pow2 < 0 && pow2 & 1) { + pow2++; + a = RSHIFT_static(a, 1); + } + pow2 = RSHIFT_static(pow2, 1); + x = a + 4194304; + y = a - 4194304; + z = 0; + cordichk(&x, &y, &z, cordic_steps, 1); + return (pow2 < 0 ? RSHIFT_dynamic(x, -pow2) : LSHIFT_dynamic(x, pow2)); +} +#endif +#ifdef LOGK +_Accum logk(_Accum a) +{ + register int8_t pow2 = 8; + _Accum x, y, z; + if(a <= 0) + return ACCUM_MIN; + /* The cordic method works only within [1, 9] + * for other values the following identity is used: + * + * log(2^n * a) = log(a) + log(2^n) = log(a) + n log(2) + */ + for(; a > 150994944; pow2++) + a = RSHIFT_static(a, 1); + for(; a < 16777216; pow2--) + a = LSHIFT_static(a, 1); + x = a + 16777216; + y = a - 16777216; + z = 0; + cordichk(&x, &y, &z, 17, 1); + return RSHIFT_static(z, 7) + LOG2k*pow2; +} +#endif +#ifdef LLOGLK +_lAccum lloglk(_lAccum a) +{ + register int8_t pow2 = 0; + _Accum x, y, z; + if(a <= 0) + return LACCUM_MIN; + /* The cordic method works only within [1, 9] + * for other values the following identity is used: + * + * log(2^n * a) = log(a) + log(2^n) = log(a) + n log(2) + */ + for(; a > 150994944; pow2++) + a = RSHIFT_static(a, 1); + for(; a < 16777216; pow2--) + a = LSHIFT_static(a, 1); + x = a + 16777216; + y = a - 16777216; + z = 0; + cordichk(&x, &y, &z, 24, 1); + return LSHIFT_static(z, 1) + LOG2lk*pow2; +} +#endif diff --git a/firmware/src/shared/avrfix/avrfix.h b/firmware/src/shared/avrfix/avrfix.h new file mode 100644 index 0000000..0f714ee --- /dev/null +++ b/firmware/src/shared/avrfix/avrfix.h @@ -0,0 +1,330 @@ +/**************************************************************** + * * + * __ ____ _________ * + * /_ \\\ \/ /| \______ * + * // \\\ /|| D /_ /. * + * // \\\_ /.|| \ ___/. * + * /___/\___\\__/. |__|\__\__.___ ___ * + * .... ....... ...|| _/_\ \////. * + * || |.| |\ ///. * + * |__|.|_|/// \ * + * .... ./__/\__\ * + * ........ * + * Fixed Point Library * + * according to * + * ISO/IEC DTR 18037 * + * * + * Version 1.0.1 * + * Maximilan Rosenblattl, Andreas Wolf 2007-02-07 * + ****************************************************************/ + +#ifndef _AVRFIX_H +#define _AVRFIX_H + +#ifndef TEST_ON_PC +#include +#include +#include +#include +#else +#ifdef linux +#include +#else +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef linux +/* 11 July 2012 + * Seeing a problem on Ubuntu 12.04 & gcc 4.6.3 whereby using the + * name _Accum causes a problem and results in the compiler error + * "two or more data types in declaration specifiers". Looks like + * something, somewhere is using the same name. Didn't spot it in + * /usr/include/. + * + * Work around by defining _Accum to __Accum after loading system + * header files. Doesn't appear to have an bad side effects. + */ +#define _Accum __Accum +#endif + +/* Only two datatypes are used from the ISO/IEC standard: + * int16_t _Accum with s7.8 bit format + * _Accum with s15.16 bit format + * int32_t _Accum with s7.24 bit format + */ + +typedef int16_t _sAccum; +typedef int32_t _Accum; +typedef int32_t _lAccum; + +/* Pragmas for defining overflow behaviour */ + +#define DEFAULT 0 +#define SAT 1 + +#ifndef FX_ACCUM_OVERFLOW +#define FX_ACCUM_OVERFLOW DEFAULT +#endif + +/* Pragmas for internal use */ + +#define SACCUM_IBIT 7 +#define SACCUM_FBIT 8 +#define ACCUM_IBIT 15 +#define ACCUM_FBIT 16 +#define LACCUM_IBIT 7 +#define LACCUM_FBIT 24 + +#define SACCUM_MIN -32767 +#define SACCUM_MAX 32767 +#define ACCUM_MIN -2147483647L +#define ACCUM_MAX 2147483647L +#define LACCUM_MIN -2147483647L +#define LACCUM_MAX 2147483647L + +#define ACCUM_INFINITY ACCUM_MAX + +#define SACCUM_FACTOR ((int16_t)1 << SACCUM_FBIT) +#define ACCUM_FACTOR ((int32_t)1 << ACCUM_FBIT) +#define LACCUM_FACTOR ((int32_t)1 << LACCUM_FBIT) + +/* Mathematical constants */ + +#define PIsk 804 +#define PIk 205887 +#define PIlk 52707179 + +#define LOG2k 45426 +#define LOG2lk 11629080 + +#define LOG10k 150902 +#define LOG10lk 38630967 + +#ifndef NULL +#define NULL ((void*)0) +#endif + +/* conversion Functions */ + +#define itosk(i) ((_sAccum)(i) << SACCUM_FBIT) +#define itok(i) ((_Accum)(i) << ACCUM_FBIT) +#define itolk(i) ((_lAccum)(i) << LACCUM_FBIT) + +#define sktoi(k) ((int8_t)((k) >> SACCUM_FBIT)) +#define ktoi(k) ((int16_t)((k) >> ACCUM_FBIT)) +#define lktoi(k) ((int8_t)((k) >> LACCUM_FBIT)) + +#define ktoli(k) ((int32_t)((k) >> ACCUM_FBIT)) + +#define sktok(sk) ( (_Accum)(sk) << (ACCUM_FBIT-SACCUM_FBIT)) +#define ktosk(k) ((_sAccum)((k) >> (ACCUM_FBIT-SACCUM_FBIT))) + +#define sktolk(sk) ((_lAccum)(sk) << (LACCUM_FBIT-SACCUM_FBIT)) +#define lktosk(lk) ((_sAccum)((lk) >> (LACCUM_FBIT-SACCUM_FBIT))) + +#define ktolk(k) ((_Accum)(k) << (LACCUM_FBIT-ACCUM_FBIT)) +#define lktok(lk) ((_lAccum)(lk) >> (LACCUM_FBIT-ACCUM_FBIT)) + +#define ftosk(f) ((_Accum)((f) * SACCUM_FACTOR)) +#define ftok(f) ((_Accum)((f) * ACCUM_FACTOR)) +#define ftolk(f) ((_lAccum)((f) * LACCUM_FACTOR)) + +#define sktof(sk) ((float)(sk) / SACCUM_FACTOR) +#define ktod(k) ((double)(k) / SACCUM_FACTOR) +#define lktod(lk) ((double)(lk) / SACCUM_FACTOR) +#define ktof(k) ((float)(k) / ACCUM_FACTOR) + +/* Main Functions */ + +extern _sAccum smulskD(_sAccum, _sAccum); +extern _Accum mulkD(_Accum, _Accum); +extern _lAccum lmullkD(_lAccum, _lAccum); + +extern _sAccum sdivskD(_sAccum, _sAccum); +extern _Accum divkD(_Accum, _Accum); +extern _lAccum ldivlkD(_lAccum, _lAccum); + +extern _sAccum smulskS(_sAccum, _sAccum); +extern _Accum mulkS(_Accum, _Accum); +extern _lAccum lmullkS(_lAccum, _lAccum); + +extern _sAccum sdivskS(_sAccum, _sAccum); +extern _Accum divkS(_Accum, _Accum); +extern _lAccum ldivlkS(_lAccum, _lAccum); + +#if FX_ACCUM_OVERFLOW == DEFAULT + #define smulsk(a,b) smulskD((a),(b)) + #define mulk(a,b) mulkD((a),(b)) + #define lmullk(a,b) lmullkD((a), (b)) + #define sdivsk(a,b) sdivskD((a), (b)) + #define divk(a,b) divkD((a), (b)) + #define ldivlk(a,b) ldivlkD((a), (b)) +#elif FX_ACCUM_OVERFLOW == SAT + #define smulsk(a,b) smulskS((a),(b)) + #define mulk(a,b) mulkS((a),(b)) + #define lmullk(a,b) lmullkS((a), (b)) + #define sdivsk(a,b) sdivskS((a), (b)) + #define divk(a,b) divkS((a), (b)) + #define ldivlk(a,b) ldivlkS((a), (b)) +#endif + +/* Support Functions */ + +#define mulikD(i,k) ktoi((i) * (k)) +#define mulilkD(i,lk) lktoi((i) * (lk)) + +#define divikD(i,k) ktoi(divkD(itok(i),(k))) +#define divilkD(i,lk) lktoi(ldivlkD(itolk(i),(lk))) + +#define kdiviD(a,b) divkD(itok(a),itok(b)) +#define lkdiviD(a,b) ldivlkD(itolk(a),itolk(b)) + +#define idivkD(a,b) ktoi(divkD((a),(b))) +#define idivlkD(a,b) lktoi(ldivlkD((a),(b))) + +#define mulikS(i,k) ktoi(mulkS(itok(i),(k))) +#define mulilkS(i,lk) lktoi(lmullkS(itolk(i),(lk))) + +#define divikS(i,k) ktoi(divkS(itok(i),(k))) +#define divilkS(i,lk) lktoi(ldivlkS(itolk(i),(lk))) + +#define kdiviS(a,b) divkS(itok(a),itok(b)) +#define lkdiviS(a,b) ldivlkS(itolk(a),itolk(b)) + +#define idivkS(a,b) ktoi(divkS((a),(b))) +#define idivlkS(a,b) lktoi(ldivlkS((a),(b))) + +#if FX_ACCUM_OVERFLOW == DEFAULT + #define mulik(a,b) mulikD((a),(b)) + #define mulilk(a,b) mulilkD((a),(b)) + #define divik(a,b) divikD((a),(b)) + #define divilk(a,b) divilkD((a),(b)) + #define kdivi(a,b) kdiviD((a),(b)) + #define lkdivi(a,b) lkdiviD((a),(b)) + #define idivk(a,b) idivkD((a),(b)) + #define idivlk(a,b) idivlkD((a),(b)) +#elif FX_ACCUM_OVERFLOW == SAT + #define mulik(a,b) mulikS((a),(b)) + #define mulilk(a,b) mulilkS((a),(b)) + #define divik(a,b) divikS((a),(b)) + #define divilk(a,b) divilkS((a),(b)) + #define kdivi(a,b) kdiviS((a),(b)) + #define lkdivi(a,b) lkdiviS((a),(b)) + #define idivk(a,b) idivkS((a),(b)) + #define idivlk(a,b) idivlkS((a),(b)) +#endif + +/* Abs Functions */ + +#define sabssk(f) ((f) < 0 ? (-(f)) : (f)) +#define absk(f) ((f) < 0 ? (-(f)) : (f)) +#define labslk(f) ((f) < 0 ? (-(f)) : (f)) + +/* Rounding Functions */ + +extern _sAccum roundskD(_sAccum f, uint8_t n); +extern _Accum roundkD(_Accum f, uint8_t n); +extern _lAccum roundlkD(_lAccum f, uint8_t n); + +extern _sAccum roundskS(_sAccum f, uint8_t n); +extern _Accum roundkS(_Accum f, uint8_t n); +extern _lAccum roundlkS(_lAccum f, uint8_t n); + +#if FX_ACCUM_OVERFLOW == DEFAULT + #define roundsk(f, n) roundskD((f), (n)) + #define roundk(f, n) roundkD((f), (n)) + #define roundlk(f, n) roundlkD((f), (n)) +#elif FX_ACCUM_OVERFLOW == SAT + #define roundsk(f, n) roundskS((f), (n)) + #define roundk(f, n) roundkS((f), (n)) + #define roundlk(f, n) roundlkS((f), (n)) +#endif + +/* countls Functions */ + +extern uint8_t countlssk(_sAccum f); +extern uint8_t countlsk(_Accum f); +#define countlslk(f) countlsk((f)) + +/* Special Functions */ + +#define CORDICC_GAIN 10188012 +#define CORDICH_GAIN 20258445 + +extern _Accum sqrtk_uncorrected(_Accum,int8_t,uint8_t); + +#define sqrtkD(a) mulkD(sqrtk_uncorrected(a, -8, 17), CORDICH_GAIN/256) +#define lsqrtlkD(a) lmullkD(sqrtk_uncorrected(a, 0, 24), CORDICH_GAIN) + +#define sqrtkS(a) mulkS(sqrtk_uncorrected(a, -8, 17), CORDICH_GAIN/256) +#define lsqrtlkS(a) lmullkS(sqrtk_uncorrected(a, 0, 24), CORDICH_GAIN) + +#if FX_ACCUM_OVERFLOW == DEFAULT + #define sqrtk(a) sqrtkD(a) + #define lsqrtlk(a) lsqrtlkD(a) +#else + #define sqrtk(a) sqrtkS(a) + #define lsqrtlk(a) lsqrtlkS(a) +#endif + +extern _Accum sincosk(_Accum, _Accum*); +extern _lAccum lsincoslk(_lAccum, _lAccum*); +extern _lAccum lsincosk(_Accum, _lAccum*); +extern _sAccum ssincossk(_sAccum, _sAccum*); + +#define sink(a) sincosk((a), NULL) +#define lsinlk(a) lsincoslk((a), NULL) +#define lsink(a) lsincosk((a), NULL) +#define ssinsk(a) ssincossk((a), NULL) + +#define cosk(a) sink((a) + PIk/2 + 1) +#define lcoslk(a) lsinlk((a) + PIlk/2) +#define lcosk(a) lsink((a) + PIk/2 + 1) +#define scossk(a) ssinsk((a) + PIsk/2) + +extern _Accum tankD(_Accum); +extern _lAccum ltanlkD(_lAccum); +extern _lAccum ltankD(_Accum); + +extern _Accum tankS(_Accum); +extern _lAccum ltanlkS(_lAccum); +extern _lAccum ltankS(_Accum); + +#if FX_ACCUM_OVERFLOW == DEFAULT + #define tank(a) tankD((a)) + #define ltanlk(a) ltanlkD((a)) + #define ltank(a) ltankD((a)) +#elif FX_ACCUM_OVERFLOW == SAT + #define tank(a) tankS((a)) + #define ltanlk(a) ltanlkS((a)) + #define ltank(a) ltankS((a)) +#endif + +extern _Accum atan2k(_Accum, _Accum); +extern _lAccum latan2lk(_lAccum, _lAccum); + +#define atank(a) atan2k(itok(1), (a)) +#define latanlk(a) latan2lk(itolk(1), (a)) + +extern _Accum logk(_Accum); +extern _lAccum lloglk(_lAccum); + +#define log2k(x) (divk(logk((x)), LOG2k)) +#define log10k(x) (divk(logk((x)), LOG10k)) +#define logak(a, x) (divk(logk((x)), logk((a)))) + +#define llog2lk(x) (ldivlk(lloglk((x)), LOG2lk)) +#define llog10lk(x) (ldivlk(lloglk((x)), LOG10lk)) +#define llogalk(a, x) (ldivlk(lloglk((x)), lloglk((a)))) + +#ifdef __cplusplus +} +#endif + +#endif /* _AVRFIX_H */ + diff --git a/firmware/src/shared/avrfix/avrfix_config.h b/firmware/src/shared/avrfix/avrfix_config.h new file mode 100644 index 0000000..f48a33f --- /dev/null +++ b/firmware/src/shared/avrfix/avrfix_config.h @@ -0,0 +1,42 @@ +/**************************************************************** + * * + * __ ____ _________ * + * /_ \\\ \/ /| \______ * + * // \\\ /|| D /_ /. * + * // \\\_ /.|| \ ___/. * + * /___/\___\\__/. |__|\__\__.___ ___ * + * .... ....... ...|| _/_\ \////. * + * || |.| |\ ///. * + * |__|.|_|/// \ * + * .... ./__/\__\ * + * ........ * + * Fixed Point Library * + * according to * + * ISO/IEC DTR 18037 * + * * + * Version 1.0.1 * + * Maximilan Rosenblattl, Andreas Wolf 2007-02-07 * + ****************************************************************/ + +#ifndef _AVRFIX_CONFIG_H +#define _AVRFIX_CONFIG_H + +#define AVR_CONFIG 0 + +#define BIG_ENDIAN 0 +#define LITTLE_ENDIAN 1 + +#ifndef AVRFIX_CONFIG +#define AVRFIX_CONFIG AVR_CONFIG +#endif + +#if AVRFIX_CONFIG == AVR_CONFIG +#define BYTE_ORDER BIG_ENDIAN +#define LSHIFT_static(x, b) ((b) == 1 ? (x) + (x) : ((b) < 8 ? ((x) << (b)) : (x) * (1UL << (b)))) +#define RSHIFT_static(x, b) ((x) >> (b)) +#define LSHIFT_dynamic(x, b) ((x) << (b)) +#define RSHIFT_dynamic(x, b) ((x) >> (b)) +#endif + +#endif /* _AVRFIX_CONFIG_H */ + diff --git a/firmware/src/shared/avrfix/lgpl.txt b/firmware/src/shared/avrfix/lgpl.txt new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/firmware/src/shared/avrfix/lgpl.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/firmware/src/shared/avrfix/readme.txt b/firmware/src/shared/avrfix/readme.txt new file mode 100644 index 0000000..0cb5ef7 --- /dev/null +++ b/firmware/src/shared/avrfix/readme.txt @@ -0,0 +1,90 @@ +============================================================================= +Readme for Fixed Point Library for Atmel 8 bit Processors +============================================================================= + __ ____ _________ + /_ \\\ \/ /| \______ + // \\\ /|| D /_ /. + // \\\_ /.|| \ ___/. + /___/\___\\__/. |__|\__\__.___ ___ + .... ....... ...|| _/_\ \////. + || |.| |\ ///. +Content: |__|.|_|/// \ +- About .... ./__/\__\ +- Requirements ........ +- Files +- Compiling +- Usage + + +----------------------------------------------------------------------------- +About +----------------------------------------------------------------------------- + +This library is dedicated for use with Atmel 8 bit Processors and is designed +according to ISO/IEC paper DTR 18037. +For Details please use the documents in the 'docu' directory. + + +----------------------------------------------------------------------------- +Requirements +----------------------------------------------------------------------------- + +- gcc +- AVRtools is required for compiling and linking +- ar for building the library +- make, if the makefile is being used + + +----------------------------------------------------------------------------- +Files +----------------------------------------------------------------------------- + +All Files of the library itself are in the directory named 'avrfix'. Those +Files are: +avrfix.c implementation of the library +avrfix.h headerfile of the library +avrfix_config.h headerfile for configuring the library +Makefile to make the library +sizes.txt a list of function sizes +srecsize.c used to get the codesize of functions +lgpl.txt the licence of this project + + +----------------------------------------------------------------------------- +Compiling +----------------------------------------------------------------------------- + +To use the library, it must be compiled first. It is recommended to use the +included Makefile, although it may be edited before it can be used. +First, the target architecture must be defined: + +# target architecture +MCU = atmega16 + +By default, the target architecture is the atmega16. + +Also the path to some tools is needed: + +# Tools +CC = avr-gcc +AR = ar +AS = avr-as +ASLD = avr-gcc -x assembler +LD = avr-ld +OBJCOPY = avr-objcopy +SRECSIZE = ./srecsize + +After editing the Makefile, the library can be compiled by simply calling +'make'. The library is then compiled and archived into the file 'libavrfix.a', +if the value of PROJNAME is not changed in the Makefile. + + +------------------------------------------------------------------------------ +Usage +------------------------------------------------------------------------------ + +To use the library, only the headerfile and the library itself is needed. +When compiling a project that uses the library, the library file needs to be +linked with the following options: + + -l avrfix -Wl,-Map=.map,-L= -mmcu= \ No newline at end of file diff --git a/firmware/src/skeinforge_plugins/altshell.py b/firmware/src/skeinforge_plugins/altshell.py new file mode 100644 index 0000000..437a82b --- /dev/null +++ b/firmware/src/skeinforge_plugins/altshell.py @@ -0,0 +1,146 @@ +""" +Dan Newman +10 April 2012 +dan dot newman at mtbaldy dot us + +Altshell is a script to cause the outside perimeter of an object to be printed +with the valve closed. The purpose is to allow downstream processing to +identify gcode for the outside shells of an object by spotting segments to be +printed with a closed valve state. + +To install the altshell script, move altshell.py to the directory + + skein_engines/skeinforge-VERSION/skeinforge_application/skeinforge_plugins/craft_plugins/ + +Then edit the file + + skein_engines/skeinforge-VERSION/skeinforge_application/skeinforge_plugins/profile_plugins/extrusion.py + +and add the altshell script to the tool chain sequence by inserting 'altshell' +into the plugin sequence in getCraftSequence(). Place 'altshell' before the +'outline' and 'skirt' plugins as they may inject themselves into the perimeter +without marking themselves as not being part of the perimeter. + +==Operation== +The default 'Activate Altshell' checkbox is off, enable it if you would like an outline printed. + +==Settings== +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from skeinforge_application.skeinforge_utilities import skeinforge_profile +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import archive +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +import sys + +__author__ = "Len Trigg (lenbok@gmail.com)" +__date__ = "$Date: 2010/11/20 $" +__license__ = "GPL 3.0" + +def getCraftedText( fileName, text='', repository=None ): + "Alternate shell text." + return getCraftedTextFromText( archive.getTextIfEmpty( fileName, text ), repository ) + +def getCraftedTextFromText( gcodeText, repository=None ): + "Alternate shell text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'altshell' ): + return gcodeText + if repository == None: + repository = settings.getReadRepository( AltshellRepository() ) + if not repository.activateAltshell.value: + return gcodeText + return AltshellSkein().getCraftedGcode( gcodeText, repository ) + +def getNewRepository(): + "Get the repository constructor." + return AltshellRepository() + +def writeOutput( fileName = ''): + "Alternate shell file." + fileName = fabmetheus_interpret.getFirstTranslatorFileNameUnmodified(fileName) + if fileName == '': + return + skeinforge_craft.writeChainTextWithNounMessage( fileName, 'altshell') + +class AltshellRepository: + "A class to handle the altshell settings." + def __init__( self ): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository( 'skeinforge_tools.craft_plugins.altshell.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Altshell', self, '' ) + self.activateAltshell = settings.BooleanSetting().getFromValue( 'Activate Altshell', self, False ) + self.executeTitle = 'Altshell' + + def execute( self ): + "Altshell button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode( self.fileNameInput.value, fabmetheus_interpret.getImportPluginFilenames(), self.fileNameInput.wasCancelled ) + for fileName in fileNames: + writeOutput( fileName ) + +class AltshellSkein: + "A class to print the outermost shell with the valve closed." + def __init__( self ): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.state = 0 + + def getCraftedGcode( self, gcodeText, repository ): + "Parse gcode text and add the altshell gcode." + self.repository = repository + self.lines = archive.getTextLines( gcodeText ) + for line in self.lines: + self.parseLine( line ) + return self.distanceFeedRate.output.getvalue() + + def parseLine( self, line ): + """" + Parse a gcode line and add it to the altshell skein. + + We want to place the close valve (M127) command after the first M101 command. + So doing ensures that any outline or skirt is printed before the valve is + closed. + """ + + splitLine = line.split() + if len( splitLine ) < 1: + return + + firstWord = splitLine[ 0 ] + + if line == '( outer )' or line == '( inner )': + self.state = 1 + + elif firstWord == '()': + if self.state == 3: + # Open valve command + self.distanceFeedRate.addLine( 'M126' ) + self.state = 0 + + elif firstWord == 'M101': + if self.state == 1: + # Found first M101 for outer perimeter + self.state = 2 + + self.distanceFeedRate.addLine( line ) + if self.state == 2: + # Close valve command + self.distanceFeedRate.addLine( 'M127' ) + self.state = 3 + +def main(): + "Display the altshell dialog." + if len( sys.argv ) > 1: + writeOutput( ' '.join( sys.argv[ 1 : ] ) ) + else: + settings.startMainLoopFromConstructor( getRepositoryConstructor() ) + +if __name__ == "__main__": + main() diff --git a/firmware/src/skeinforge_plugins/extrusion-35.py b/firmware/src/skeinforge_plugins/extrusion-35.py new file mode 100644 index 0000000..0a4fa6c --- /dev/null +++ b/firmware/src/skeinforge_plugins/extrusion-35.py @@ -0,0 +1,64 @@ +""" +This page is in the table of contents. +Extrusion is a script to set the extrusion profile for the skeinforge chain. + +The displayed craft sequence is the sequence in which the tools craft the model and export the output. + +On the extrusion dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if ABS is selected and the name ABS_black is in the input field, clicking the 'Add Profile' button will duplicate ABS and save it as ABS_black. The 'Delete Profile' button deletes the selected profile. + +The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles. + +To change the extrusion profile, in a shell in the profile_plugins folder type: +> python extrusion.py + +An example of using extrusion from the python interpreter follows below. + + +> python +Python 2.5.1 (r251:54863, Sep 22 2007, 01:43:31) +[GCC 4.2.1 (SUSE Linux)] on linux2 +Type "help", "copyright", "credits" or "license" for more information. +>>> import extrusion +>>> extrusion.main() +This brings up the extrusion setting dialog. + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GPL 3.0' + + +def getCraftSequence(): + 'Get the extrusion craft sequence.' + return 'carve,bottom,preface,widen,inset,fill,multiply,speed,temperature,raft,chamber,tower,jitter,clip,stretch,comb,cool,hop,wipe,oozebane,altshell,outline,splodge,home,lash,fillet,limit,reversal,dimension,unpause,export'.split(',') + +def getNewRepository(): + 'Get the repository constructor.' + return ExtrusionRepository() + + +class ExtrusionRepository: + 'A class to handle the export settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsSetCraftProfile( getCraftSequence(), 'ABS', self, 'skeinforge_plugins.profile_plugins.extrusion.html') + + +def main(): + 'Display the export dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor( getNewRepository() ) + +if __name__ == '__main__': + main() diff --git a/firmware/src/skeinforge_plugins/extrusion-40.py b/firmware/src/skeinforge_plugins/extrusion-40.py new file mode 100644 index 0000000..92d0796 --- /dev/null +++ b/firmware/src/skeinforge_plugins/extrusion-40.py @@ -0,0 +1,53 @@ +""" +This page is in the table of contents. +Extrusion is a script to set the extrusion profile for the skeinforge chain. + +The displayed craft sequence is the sequence in which the tools craft the model and export the output. + +On the extrusion dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if ABS is selected and the name ABS_black is in the input field, clicking the 'Add Profile' button will duplicate ABS and save it as ABS_black. The 'Delete Profile' button deletes the selected profile. + +The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles. + +To change the extrusion profile, in a shell in the profile_plugins folder type: +> python extrusion.py + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftSequence(): + 'Get the extrusion craft sequence.' + return 'carve scale bottom preface widen inset fill multiply speed temperature raft altshell skirt chamber tower jitter clip stretch comb cool hop wipe oozebane splodge home lash fillet limit dimension unpause reversal export'.split() + +def getNewRepository(): + 'Get new repository.' + return ExtrusionRepository() + + +class ExtrusionRepository: + 'A class to handle the export settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsSetCraftProfile( getCraftSequence(), 'ABS', self, 'skeinforge_plugins.profile_plugins.extrusion.html') + + +def main(): + 'Display the export dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor( getNewRepository() ) + +if __name__ == '__main__': + main() diff --git a/firmware/src/skeinforge_plugins/extrusion-44.py b/firmware/src/skeinforge_plugins/extrusion-44.py new file mode 100644 index 0000000..d98fe67 --- /dev/null +++ b/firmware/src/skeinforge_plugins/extrusion-44.py @@ -0,0 +1,53 @@ +""" +This page is in the table of contents. +Extrusion is a script to set the extrusion profile for the skeinforge chain. + +The displayed craft sequence is the sequence in which the tools craft the model and export the output. + +On the extrusion dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if ABS is selected and the name ABS_black is in the input field, clicking the 'Add Profile' button will duplicate ABS and save it as ABS_black. The 'Delete Profile' button deletes the selected profile. + +The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles. + +To change the extrusion profile, in a shell in the profile_plugins folder type: +> python extrusion.py + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftSequence(): + 'Get the extrusion craft sequence.' + return 'carve scale bottom preface widen inset fill multiply speed temperature raft altshell skirt chamber tower jitter clip stretch skin comb cool hop wipe oozebane splodge home lash fillet limit dimension unpause bookend export'.split() + +def getNewRepository(): + 'Get new repository.' + return ExtrusionRepository() + + +class ExtrusionRepository: + 'A class to handle the export settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsSetCraftProfile( getCraftSequence(), 'ABS', self, 'skeinforge_plugins.profile_plugins.extrusion.html') + + +def main(): + 'Display the export dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/firmware/src/skeinforge_plugins/extrusion-47.py b/firmware/src/skeinforge_plugins/extrusion-47.py new file mode 100644 index 0000000..525aa36 --- /dev/null +++ b/firmware/src/skeinforge_plugins/extrusion-47.py @@ -0,0 +1,53 @@ +""" +This page is in the table of contents. +Extrusion is a script to set the extrusion profile for the skeinforge chain. + +The displayed craft sequence is the sequence in which the tools craft the model and export the output. + +On the extrusion dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if ABS is selected and the name ABS_black is in the input field, clicking the 'Add Profile' button will duplicate ABS and save it as ABS_black. The 'Delete Profile' button deletes the selected profile. + +The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles. + +To change the extrusion profile, in a shell in the profile_plugins folder type: +> python extrusion.py + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftSequence(): + 'Get the extrusion craft sequence.' + return 'carve scale bottom preface widen inset fill multiply speed temperature raft altshell skirt chamber tower jitter clip smooth stretch skin comb cool hop wipe oozebane splodge home lash fillet limit unpause dimension alteration export'.split() + +def getNewRepository(): + 'Get new repository.' + return ExtrusionRepository() + + +class ExtrusionRepository: + 'A class to handle the export settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsSetCraftProfile( getCraftSequence(), 'ABS', self, 'skeinforge_application.skeinforge_plugins.profile_plugins.extrusion.html') + + +def main(): + 'Display the export dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/firmware/src/skeinforge_plugins/extrusion-50.py b/firmware/src/skeinforge_plugins/extrusion-50.py new file mode 100644 index 0000000..81432a7 --- /dev/null +++ b/firmware/src/skeinforge_plugins/extrusion-50.py @@ -0,0 +1,53 @@ +""" +This page is in the table of contents. +Extrusion is a script to set the extrusion profile for the skeinforge chain. + +The displayed craft sequence is the sequence in which the tools craft the model and export the output. + +On the extrusion dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if ABS is selected and the name ABS_black is in the input field, clicking the 'Add Profile' button will duplicate ABS and save it as ABS_black. The 'Delete Profile' button deletes the selected profile. + +The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles. + +To change the extrusion profile, in a shell in the profile_plugins folder type: +> python extrusion.py + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftSequence(): + 'Get the extrusion craft sequence.' + return 'carve scale bottom preface widen inset fill multiply speed temperature raft altshell skirt chamber tower jitter clip smooth stretch skin comb cool hop wipe oozebane dwindle splodge home lash fillet limit unpause dimension alteration export'.split() + +def getNewRepository(): + 'Get new repository.' + return ExtrusionRepository() + + +class ExtrusionRepository: + 'A class to handle the export settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsSetCraftProfile( getCraftSequence(), 'ABS', self, 'skeinforge_application.skeinforge_plugins.profile_plugins.extrusion.html') + + +def main(): + 'Display the export dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/firmware/src/skeinforge_plugins/install.sh b/firmware/src/skeinforge_plugins/install.sh new file mode 100755 index 0000000..b6c853a --- /dev/null +++ b/firmware/src/skeinforge_plugins/install.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Automated install for jetty altshell for ReplicatorG > V33 on OS X +# Created by Aaron Ciuffo - aaron.ciuffo@gmail.com http://txoof.com +# +# This script should copy in the appropriate altshell files for your +# version of ReplicatorG. This script should work with versions of RepG > V33. +# + +#Path to ReplicatorG +REPG=/Applications/ReplicatorG.app +#Base path to machine profiles +SFENGINE="$REPG/Contents/Resources/skein_engines/skeinforge-" +#Base path to craft directory +SFPLUGIN="skeinforge_application/skeinforge_plugins/craft_plugins/" +#Base path to profile plugins +SFPROFILE="skeinforge_application/skeinforge_plugins/profile_plugins/" + +if [ -e $REPG ]; then + echo "Found ReplicatorG" + echo "Copying appropriate altshell profiles into ReplicatorG" + for FILE in ./extrusion*.py + do + VERSION=`echo $FILE|cut -d"-" -f2|cut -d"." -f1` + if [ -d $SFENGINE$VERSION ]; then + echo "Copying altshell.py for version $VERSION" + cp ./altshell.py $SFENGINE$VERSION/$SFPLUGIN + #make a backup of the extrusion.py + echo "Copying extrusion.py for version: $VERSION" + cp $SFENGINE$VERSION/$SFPROFILE/extrusion.py $SFENGINE$VERSION/$SFPROFILE/extrusion.py.old + echo "Backup of extrusion.py stored as extrusion.py.old" + cp $FILE $SFENGINE$VERSION/$SFPROFILE/extrusion.py + fi + done +else + echo "Could not find $REPG; exiting" + exit 0 +fi diff --git a/tests/g3_tests b/tests/g3_tests new file mode 120000 index 0000000..49a9ed4 --- /dev/null +++ b/tests/g3_tests @@ -0,0 +1 @@ +../firmware/tests \ No newline at end of file diff --git a/tests/s3g_tests/G3FirmwareTests.py b/tests/s3g_tests/G3FirmwareTests.py new file mode 100644 index 0000000..f4992d9 --- /dev/null +++ b/tests/s3g_tests/G3FirmwareTests.py @@ -0,0 +1,927 @@ +#!/usr/bin/python + +import unittest +import optparse +import serial +import io +import struct +import array +import time +import os, sys +import logging +import tempfile +import inspect + +lib_path = os.path.abspath('./s3g') +sys.path.append(lib_path) + +# The s3g API +import s3g + +# The s3g functions to test and the call arguments to use +from test_s3g_functions import * + +# Testing utility routines +from test_utilities import * + +# Defaults +extensive = True +port = '/dev/ttyACM0' +hasInterface = True + +firmware = 'G3Firmware' +#firmware = 'Jetty' +#firmware = 'MightyBoard' + +# Process our command line arguments now +# we need to know which firmware we will be testing in order to know +# which profiles to load + +if __name__ == '__main__': + + parser = optparse.OptionParser() + parser.add_option("-e", "--extensive", dest="extensive", default=str(extensive)) + parser.add_option("-m", "--mightyboard", dest="isMightyBoard", default="True") + parser.add_option("-f", "--firmware", dest="firmware", default=firmware) + parser.add_option("-i", "--interface", dest="hasInterface", default="True") + parser.add_option("-p", "--port", dest="port", default=port) + (options, args) = parser.parse_args() + + if options.extensive.lower() == "false": + print "Foregoing Heater Tests" + extensive = False + else: + extensive = True + + if options.hasInterface.lower() in ['0', 'false', 'no']: + print "Foregoing tests requiring Interface Boards" + hasInterface = False + + port = options.port + firmware = options.firmware + +# Now load the firmware 'profiles' + +if firmware.lower() in ['g3firmware', 'jetty']: + + from G3Firmware_constants import * + from G3Firmware_unsupported_functions import * + hasInterface = False + +else: + + from MightyBoard_constants import * + from MightyBoard_unsupported_functions import * + +class commonFunctionTests(unittest.TestCase): + + def test_ConvertFromNUL(self): + b = bytearray("asdf\x00") + expectedReturn = "asdf" + self.assertEqual(expectedReturn, ConvertFromNUL(b)) + +""" +Test core packet handling routines used by the s3g module + -- test encode/decode functionality + -- test error handling (malformed packets, etc.) +""" + +class s3gPacketTests(unittest.TestCase): + + def setUp(self): + self.r = s3g.s3g() + self.r.writer = initWriterComms(port, 115200, timeout=1) + + def tearDown(self): + self.r.writer.file.close() + self.r = None + + def GetVersionPayload(self): + payload = struct.pack('` + + +## Common Tests (commonFunctionTests) + + % G3FirmwareTests.py -f Jetty -p /dev/tty.usbmodemfa131 + test_ConvertFromNUL (__main__.commonFunctionTests) ... ok + ---------------------------------------------------------------------- + Ran 1 test in 0.000s + OK + +## Packet Tests (s3gPacketTests) +#### expected timeout errors not shown for brevity + + % G3FirmwareTests.py -f Jetty -p /dev/tty.usbmodemfa131 + test_BadCRC (__main__.s3gPacketTests) ... ok + test_EmptyPacket (__main__.s3gPacketTests) ... ok + test_GetVersionPacket (__main__.s3gPacketTests) ... ok + test_GetVersionPayload (__main__.s3gPacketTests) ... ok + test_LongLength (__main__.s3gPacketTests) ... ok + test_LongPayload (__main__.s3gPacketTests) ... ok + test_MaxLength (__main__.s3gPacketTests) ... ok + test_NoHeader (__main__.s3gPacketTests) ... ok + test_OversizedLength (__main__.s3gPacketTests) ... ok + test_PreceedingPacket (__main__.s3gPacketTests) ... ok + test_ShortLength (__main__.s3gPacketTests) ... ok + test_ShortPayload (__main__.s3gPacketTests) ... ok + test_TrailingPacket (__main__.s3gPacketTests) ... ok + ---------------------------------------------------------------------- + Ran 13 tests in 181.614s + OK + + +## Send Receive Tests (s3gSendReceiveTests) + + % G3FirmwareTests.py -f Jetty -p /dev/tty.usbmodemfa131 + test_s3g_functions (__main__.s3gSendReceiveTests) ... Invoking abort_immediately() + Invoking build_end_notification() + Invoking build_start_notification(aTest) + Skipping capture_to_file() + Invoking change_tool(0) + Invoking clear_buffer() + Invoking delay(10) + Invoking display_message(0, 0, 'TESTING', 1, False, False, False) + Skipping end_capture_to_file() + Invoking extended_stop(True, True) + Invoking find_axes_maximums([], 1, 0) + Invoking find_axes_minimums([], 1, 0) + Invoking get_advanced_version() + Invoking get_available_buffer_size() + Invoking get_build_name() + Invoking get_build_stats() + Invoking get_communication_stats() + Invoking get_extended_position() + Invoking get_motherboard_status() + Invoking get_motor1_speed(0) + Invoking get_motor1_speed_PWM(0) + Invoking get_next_filename(False) + Invoking get_platform_target_temperature(0) + Invoking get_platform_temperature(0) + Invoking get_position() + Invoking get_tool_status(0) + Invoking get_toolhead_target_temperature(0) + Invoking get_toolhead_temperature(0) + Invoking get_toolhead_version(0) + Invoking get_version() + Invoking init() + Invoking is_finished() + Invoking is_platform_ready(0) + Invoking is_tool_ready(0) + Invoking pause() + Skipping playback_capture() + Invoking queue_extended_point([0, 0, 0, 0, 0], 500) + Invoking queue_extended_point_new([0, 0, 0, 0, 0], 1, ['X', 'Y', 'Z', 'A', 'B']) + Invoking queue_point_absolute([0, 0, 0], 500) + Invoking queue_song(1) + Invoking read_from_EEPROM(0, 1) + Invoking read_from_toolhead_EEPROM(0, 0, 0) + Invoking recall_home_positions(['X', 'Y', 'Z', 'A', 'B'],) + Invoking reset() + Invoking reset_to_factory() + Invoking set_RGB_LED(255, 0, 0, 0) + Invoking set_beep(1000, 3) + Invoking set_build_percent(100) + Invoking set_extended_position([0, 0, 0, 0, 0],) + Invoking set_motor1_direction(0, False) + Invoking set_motor1_speed_PWM(0, 0) + Invoking set_motor1_speed_RPM(0, 0) + Invoking set_platform_temperature(0, 100) + Invoking set_position([0, 0, 0],) + Invoking set_potentiometer_value('x', 118) + Invoking set_servo1_position(0, 10) + Invoking set_servo2_position(0, 10) + Invoking set_toolhead_temperature(0, 100) + Invoking store_home_positions(['X', 'Y', 'Z', 'A', 'B'],) + Invoking toggle_abp(0, False) + Invoking toggle_axes(['X', 'Y', 'Z', 'A', 'B'], True) + Invoking toggle_extra_output(0, False) + Invoking toggle_fan(0, False) + Invoking toggle_motor1(0, True, True) + Invoking tool_action_command(0, 23) + Invoking tool_query(0, 0) + Invoking toolhead_abort(0) + Invoking toolhead_init(0) + Invoking toolhead_pause(0) + Invoking wait_for_button('up', 0, True, False, False) + Invoking wait_for_platform_ready(0, 100, 50) + Invoking wait_for_tool_ready(0, 100, 50) + Invoking write_to_EEPROM(4092, '\xff') + Invoking write_to_toolhead_EEPROM(0, 508, '\xff') + ok + ---------------------------------------------------------------------- + Ran 1 test in 20.499s + OK + +## Function Tests (s3gFunctionTests) + + % G3FirmwareTests.py -f Jetty -p /dev/tty.usbmodemfa131 + test_AbortImmediately (__main__.s3gFunctionTests) ... ok + test_ClearBuffer (__main__.s3gFunctionTests) ... ok + test_CommStats (__main__.s3gFunctionTests) ... ok + test_Delay (__main__.s3gFunctionTests) ... 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + ok + test_ExtendedStop (__main__.s3gFunctionTests) ... ok + test_FindAxesMaximums (__main__.s3gFunctionTests) ... + Please move the platform and extruder away from any endstops and then press enter to continue. + ok + test_FindAxesMinimums (__main__.s3gFunctionTests) ... + Please move the platform and extruder away from any endstops and then press enter to continue. + ok + test_GetAvailableBufferSize (__main__.s3gFunctionTests) ... ok + test_Init (__main__.s3gFunctionTests) ... ok + test_IsFinished (__main__.s3gFunctionTests) ... ok + test_IsPlatformReady (__main__.s3gFunctionTests) ... ok + test_IsToolReady (__main__.s3gFunctionTests) ... ok + test_QueueExtendedPoint (__main__.s3gFunctionTests) ... ok + test_QueueExtendedPointNew (__main__.s3gFunctionTests) ... ok + test_ReadWordsFromEeprom (__main__.s3gFunctionTests) ... ok + test_RecallHomePositions (__main__.s3gFunctionTests) ... ok + test_Reset (__main__.s3gFunctionTests) ... ok + test_SetGetExtendedPosition (__main__.s3gFunctionTests) ... ok + test_SetGetPlatformTargetTemperature (__main__.s3gFunctionTests) ... ok + test_SetGetToolheadTargetTemperature (__main__.s3gFunctionTests) ... ok + test_StoreHomePositions (__main__.s3gFunctionTests) ... ok + test_WriteToEeprom (__main__.s3gFunctionTests) ... ok + ---------------------------------------------------------------------- + Ran 22 tests in 191.835s + OK + +## User Function Tests (s3gUserFunctionTests) + + % G3FirmwareTests.py -f Jetty -p /dev/tty.usbmodemfa131 + test_GetToolStatus (__main__.s3gUserFunctionTests) ... + Please turn the bot off and unplug extruder 0's thermocouple!! Press enter to continue. + + Please turn the bot off and plug in the platform and extruder 0's thermocouple!! Press enter to continue. + ok + test_GetToolheadVersion (__main__.s3gUserFunctionTests) ... + What is the version number of toolhead 0 on your bot? 3.1 + ok + test_GetVersion (__main__.s3gUserFunctionTests) ... + What is the version number of your bot? 3.1 + ok + test_ToggleAxes (__main__.s3gUserFunctionTests) ... + Please try to move all (x,y,z) the axes! Can you move them without using too much force? (y/n) n + + Please try to move all (x,y,z) the axes! Can you move them without using too much force? (y/n) y + ok + test_ToggleFan (__main__.s3gUserFunctionTests) ... + Is toolhead 0's fan on? (y/n) y + ok + ---------------------------------------------------------------------- + Ran 5 tests in 102.575s + OK + +## Sd Card Tests (s3gSDCardTests) + + % G3FirmwareTests.py -f Jetty -p /dev/tty.usbmodemfa131 + test_CaptureToFile (__main__.s3gSDCardTests) ... ok + test_CaptureToFileReply (__main__.s3gSDCardTests) ... ok + test_EndCaptureToFile (__main__.s3gSDCardTests) ... ok + test_EndCaptureToFileReply (__main__.s3gSDCardTests) ... ok + test_GetBuildName (__main__.s3gSDCardTests) ... + Please load the test SD card into the machine, select one of the files and begin to print it. Then type the name _exactly_ as it appears on the bot's screen. box_1.s3g + ok + test_GetNextFilename (__main__.s3gSDCardTests) ... + Please make sure the only files on the SD card plugged into the bot are the files inside the testFiles directory!! Press enter to continue + ok + test_PlaybackCapture (__main__.s3gSDCardTests) ... ok + test_PlaybackCaptureReply (__main__.s3gSDCardTests) ... ok + ---------------------------------------------------------------------- + Ran 8 tests in 43.424s + OK + diff --git a/tests/s3g_tests/s3g b/tests/s3g_tests/s3g new file mode 120000 index 0000000..f99c9d9 --- /dev/null +++ b/tests/s3g_tests/s3g @@ -0,0 +1 @@ +../../../s3g \ No newline at end of file diff --git a/tests/s3g_tests/test_s3g_functions.py b/tests/s3g_tests/test_s3g_functions.py new file mode 100644 index 0000000..9ba3d2e --- /dev/null +++ b/tests/s3g_tests/test_s3g_functions.py @@ -0,0 +1,174 @@ +import os, sys +lib_path = os.path.abspath('./s3g') +sys.path.append(lib_path) +import s3g + +""" +A dictionary of s3g function names and suitable call arguments for testing. + +Entries in the dictionary are keyed by the actual name of a method +function in s3g.py, + + a = s3g.s3g() + for f in dir( a ): + if inspect.ismethod( getattr( a, f ) ): + print "I'm the %s method function of s3g.s3g()" % f + +Values in the dictionary take one of three forms: + + None + Do not attempt to call the function + + tuple + (call-args) + Call arguments to pass to the function + + list + [ (call-args), 'post-call-statements-string' ] + The first list element is the tuple of call argument. The second list element + is a string containing an instruction to execute after the function call. A + single %s will be replaced with an object of type s3g capable of calling s3g + functions. + +NOTE BENE: if there's just one call argument AND it is a list, then for the "tuple" + case above, use "([a,b,c,...],)". The extra comma after the list preserves the + tuple-ness. Without the comma, the value is reduced to "[a,b,c,...]" and becomes + indistinguishable from the "list" case above. + +Note: Yes, it's possible to design this table with a more uniform value; e.g., use + [ (args), 'post-call-string' ] for all entries. However, that's more cumbersome for + simple entries which are the majority case. It's hoped that keeping most entries + simple will help with maintainability. (OTOH, consistency could be a better choice.) +""" + +s3g_function_calls = { + +# Host Query Commands + + 'get_version' : (), # 000 + 'init' : (), # 001 + 'get_available_buffer_size' : (), # 002 + 'clear_buffer' : [ (), 'time.sleep(5)' ], # 003 + 'get_position' : (), # 004 + 'abort_immediately' : [ (), 'time.sleep(5)' ], # 007 + 'pause' : (), # 008 + 'tool_query' : (0, s3g.slave_query_command_dict['GET_VERSION']), # 010 + 'is_finished' : (), # 011 + 'read_from_EEPROM' : (0x00, 1), # 012 + 'write_to_EEPROM' : (0xFFC, '\xff'), # 013 + 'capture_to_file' : None, # 014 + 'end_capture_to_file' : None, # 015 + 'playback_capture' : None, # 016 + 'reset' : [ (), 'time.sleep(5)' ], # 017 + 'get_next_filename' : (False), # 018 + 'get_build_name' : (), # 020 + 'get_extended_position' : (), # 021 + 'extended_stop' : (True, True), # 022 + 'get_motherboard_status' : (), # 023 + 'get_build_stats' : (), # 024 + 'get_communication_stats' : (), # 025 + 'get_advanced_version' : (), # 027 + +# Host Action Commands (buffered) + + 'queue_point_absolute' : ([0, 0, 0,], 500), # 129 + 'set_position' : ([0, 0, 0],), # 130 + 'find_axes_maximums' : ([], 1, 0), # 131 + 'find_axes_minimums' : ([], 1, 0), # 132 + 'delay' : (10), # 133 + 'change_tool' : (0), # 134 + 'wait_for_tool_ready' : (0, 100, 50), # 135 + 'tool_action_command' : (0, s3g.slave_action_command_dict['PAUSE']), # 136 + 'toggle_axes' : (['X', 'Y', 'Z', 'A', 'B'], True), # 137 + 'queue_extended_point' : ([0, 0, 0, 0, 0], 500), # 139 + 'set_extended_position' : ([0, 0, 0, 0, 0],), # 140 + 'wait_for_platform_ready' : (0, 100, 50), # 141 + 'queue_extended_point_new' : ([0, 0, 0, 0, 0], 1, ['X', 'Y', 'Z', 'A', 'B']), # 142 + 'store_home_positions' : (['X', 'Y', 'Z', 'A', 'B'],), # 143 + 'recall_home_positions' : (['X', 'Y', 'Z', 'A', 'B'],), # 144 + 'set_potentiometer_value' : ('x', 118), # 145 + 'set_RGB_LED' : (255, 0, 0, 0), # 146 + 'set_beep' : (1000, 3), # 147 + 'wait_for_button' : ('up', 0, True, False, False), # 148 + 'display_message' : (0, 0, "TESTING", 1, False, False, False), # 149 + 'set_build_percent' : (100), # 150 + 'queue_song' : (1), # 151 + 'reset_to_factory' : [ (), 'time.sleep(5)' ], # 152 + 'build_start_notification' : [ ('aTest'), '%s.build_end_notification()' ], # 153 + 'build_end_notification' : (), # 154 + +# Tool Query Commands + + 'get_toolhead_version' : (0), # 000 + 'get_toolhead_temperature' : (0), # 002 + 'get_motor1_speed' : (0), # 017 + 'get_motor1_speed_PWM' : (0), # 019 + 'is_tool_ready' : (0), # 022 + 'read_from_toolhead_EEPROM' : (0, 0x00, 0), # 025 + 'write_to_toolhead_EEPROM' : (0, 0x1FC, '\xff'), # 026 + 'get_platform_temperature' : (0), # 030 + 'get_toolhead_target_temperature' : (0), # 032 + 'get_platform_target_temperature' : (0), # 033 + 'is_platform_ready' : (0), # 035 + 'get_tool_status' : (0), # 036 + 'get_PID_state' : (0), # 037 + +# Tool Action Commands + + 'toolhead_init' : (0), # 001 + 'set_toolhead_temperature' : [ (0, 100), '%s.set_toolhead_temperature(0, 0)' ], # 003 + 'set_motor1_speed_PWM' : (0, 0), # 004 + 'set_motor1_speed_RPM' : (0, 0), # 006 + 'set_motor1_direction' : (0, False), # 008 + 'toggle_motor1' : (0, True, True), # 010 + 'toggle_fan' : (0, False), # 012 + 'toggle_extra_output' : (0, False), # 013 + 'set_servo1_position' : (0, 10), # 014 + 'set_servo2_position' : (0, 10), # 015 + 'toolhead_pause' : (0), # 023 + 'toolhead_abort' : (0), # 024 + 'toggle_abp' : (0, False), # 027 + 'set_platform_temperature' : [ (0, 100), '%s.set_platform_temperature(0, 0)' ] # 031 +} + +""" +A list of s3g class methods to ignore. +""" +s3g_functions_to_ignore = [ '__init__', 'from_filename' ] + +""" +A list of the s3g functions which are buffered Host Action commands and thus always +return a success. +""" +s3g_buffered_functions = [ + 'queue_point_absolute', # 129 + 'set_position', # 130 + 'find_axes_maximums', # 131 + 'find_axes_minimums', # 132 + 'delay', # 133 + 'change_tool', # 134 + 'wait_for_tool_ready', # 135 + 'tool_action_command', # 136 + 'toggle_axes', # 137 + 'queue_extended_point', # 139 + 'set_extended_position', # 140 + 'wait_for_platform_ready', # 141 + 'queue_extended_point_new', # 142 + 'store_home_positions', # 143 + 'recall_home_positions', # 144 + 'set_potentiometer_value', # 145 + 'set_RGB_LED', # 146 + 'set_beep', # 147 + 'wait_for_button', # 148 + 'display_message', # 149 + 'set_build_percent', # 150 + 'queue_song', # 151 + 'reset_to_factory', # 152 + 'build_start_notification', # 153 + 'build_end_notification', # 154 + +# The following Tool Action commands are buffered by virtue of being issued as +# payloads to a Host Action command + + 'toolhead_abort' # 024 +] diff --git a/tests/s3g_tests/test_utilities.py b/tests/s3g_tests/test_utilities.py new file mode 100644 index 0000000..8680c50 --- /dev/null +++ b/tests/s3g_tests/test_utilities.py @@ -0,0 +1,116 @@ +import inspect +import os, sys +lib_path = os.path.abspath('./s3g') +sys.path.append(lib_path) +import s3g +import serial +import time + +import G3Firmware_constants + +""" +Evaluate the supplied string. The string may contain a '%s' which will be replaced +with the name of a s3g class object. This allows evaluation of a s3g class member +function, + + .(args) + +For example, to turn a heater off, supply the string + + %s.set_toolhead_temperature(0, 0) + +which will get changed to + + obj.set_toolhead_temperature(0, 0) + +and then evaluated. +""" +def evalStr(obj, string): + str = string + if str.find('%s') >= 0: + str = string % 'obj' + try: + eval(str) + except: + print 'Evaluating %s raised an exception, %s' % ( str, sys.exc_info()[0] ) + +""" +Call the supplied function with the specified arguments. If isSupported is False, +then a s3g.CommandNotSupportedError exception will be expected. +""" +def callFunc(func, obj, isSupported, args): + if isSupported: + if isinstance(args, tuple): + func(*args) + else: + func(args) + else: + if isinstance(args, tuple): + obj.assertRaises(s3g.CommandNotSupportedError, func, *args) + else: + obj.assertRaises(s3g.CommandNotSupportedError, func, args) + +""" +Open a serial connection to the specified port and then clear the serial port's +I/O buffers. +""" +def initSerialComms(port, speed=115200, timeout=1): + sp = serial.Serial(port, speed, timeout=timeout) + #sp.setRTS() # Helps on Windows + #sp.setDTR() # Helps on Windows + sp.flushInput() + sp.flushOutput() + time.sleep(0.1) + return sp + +""" +Open a serial connection to the specified port and then instantiate +a s3g.Writer.StreamWriter() object which uses that serial connection. +""" +def initWriterComms(port, speed=115200, timeout=1): + return s3g.Writer.StreamWriter(initSerialComms(port, speed, timeout)) + +""" +Strip the trailing NUL from the end of a NUL terminated string +""" +def ConvertFromNUL(b): + if b[-1] != 0: + raise TypeError("Cannot convert from non-NUL terminated string") + return str(b[:-1]) + +""" +Disable the stepper motors +Set the heater target temperatures to 0 +""" +def powerDown(obj): + if obj is None: + return + try: + # Turn heaters off + obj.set_platform_temperature(0, 0) + for toolhead in constants['toolheads']: + obj.set_toolhead_temperature(toolhead, 0) + # Disable stepper motors + obj.toggle_axes(['x', 'y', 'z', 'a', 'b'], false) + except: + pass + +""" +The following functions are used to implement @skipIf and @skipUnless decorators +""" +def _id(obj): + return obj + +def skip(*args, **kwargs): + if inspect.isfunction( args[0] ) or inspect.ismethod( args[0] ): + print "Skipping " + getattr(args[0], '__name__') + +def skipIf(condition): + if condition: + return skip + return _id + +def skipUnless(condition): + if not condition: + return skip + return _id From 18ebd2070d9c230dce73e1dbfcc54381565f4c05 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 14 Sep 2012 19:37:06 -0600 Subject: [PATCH 51/61] Updating copyright / attribution. --- README.TXT | 48 ++++++++++++++++++++++ firmware/COPYING | 45 ++++++++++++++++++++ firmware/README.txt | 46 +++++++++++++++++++++ firmware/src/shared/StepperAccel.cc | 11 +++-- firmware/src/shared/StepperAccel.hh | 5 +++ firmware/src/shared/StepperAccelPlanner.cc | 9 +++- firmware/src/shared/StepperAccelPlanner.hh | 5 +++ 7 files changed, 164 insertions(+), 5 deletions(-) diff --git a/README.TXT b/README.TXT index 1a8362e..8e7e05f 100644 --- a/README.TXT +++ b/README.TXT @@ -15,3 +15,51 @@ This project contains the following subdirectories: damage during bootloader startup. * dist - this directory contains tools and scripts for quickly installing bootloaders and board images on Piles O' Boards. + +----- + +This software incorporates code related to acceleration from Marlin: https://github.com/ErikZalm/Marlin + +----- + +This software is covered by GNU General Public License v3 and according +to section 7.), subsection b), additional permissions for author +attribution are required on any work that incorporates, is derived +or inspired from the following components: + +a) JKN Advance +b) YAJ (Yet Another Jerk) +c) Altshell ReplicatorG plugin +d) Pause @ ZPos +e) Advance Pressure Relax + + +Author attribution is required as follows: + + 1. If the device this software is executed on has an LCD or + display screen attached, credit/attribution must be provided + on this screen with at least 1 second duration when and each + time the device is powered on. + + 2. If the device does not have a display screen attached and the + software is distributed in binary form, then credit/attribution + is required to be displayed to the user prior to installation of + the binary software. + + 3. If the software is being supplied in source code form, then any + existing credit/attributions must be retained. + + 4. If the software is being supplied in source code form, but it is + derived or inspired from this source code, then Credit/Attribution + below must be provided in the source code near the top of the source + file. + + 5. The additional permissions listed here are required to be included + in their entirety with any license file of any derivative works that + use the above features. + +Credit/Attribution: + +This software uses the following components from Jetty Firmware: +(LIST OF COMPONENTS GOES HERE) +authored by Dan Newman and Jetty. diff --git a/firmware/COPYING b/firmware/COPYING index 94a9ed0..50742ac 100644 --- a/firmware/COPYING +++ b/firmware/COPYING @@ -1,3 +1,48 @@ +This software is covered by GNU General Public License v3 and according +to section 7.), subsection b), additional permissions for author +attribution are required on any work that incorporates, is derived +or inspired from the following components: + +a) JKN Advance +b) YAJ (Yet Another Jerk) +c) Altshell ReplicatorG plugin +d) Pause @ ZPos +e) Advance Pressure Relax + + +Author attribution is required as follows: + + 1. If the device this software is executed on, has an LCD or + display screen attached, credit/attribution must be provided + on this screen with at least 1 second duration when the device is + powered on. + + 2. If the device does not have a display screen attached and the + software is distributed in binary form, then credit/attribution + is required to be displayed to the user prior to installation of + the binary software. + + 3. If the software is being supplied in source code form, then any + existing credit/attributions must be retained. + + 4. If the software is being supplied in source code form, but it is + derived or inspired from this source code, then Credit/Attribution + below must be provided in the source code near the top of the source + file. + + 5. The additional permissions listed here are required to be included + in their entirety with any license file of any derivative works that + use the above features. + +Credit/Attribution: + +This software uses the following components from Jetty Firmware: +(LIST OF COMPONENTS GOES HERE) +authored by Dan Newman and Jetty. + +-------- + + GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 diff --git a/firmware/README.txt b/firmware/README.txt index 55308d3..bce6b7c 100644 --- a/firmware/README.txt +++ b/firmware/README.txt @@ -1,3 +1,49 @@ +This software is covered by GNU General Public License v3 and according +to section 7.), subsection b), additional permissions for author +attribution are required on any work that incorporates, is derived +or inspired from the following components: + +a) JKN Advance +b) YAJ (Yet Another Jerk) +c) Altshell ReplicatorG plugin +d) Pause @ ZPos +e) Advance Pressure Relax + + +Author attribution is required as follows: + + 1. If the device this software is executed on, has an LCD or + display screen attached, credit/attribution must be provided + on this screen with at least 1 second duration when the device is + powered on. + + 2. If the device does not have a display screen attached and the + software is distributed in binary form, then credit/attribution + is required to be displayed to the user prior to installation of + the binary software. + + 3. If the software is being supplied in source code form, then any + existing credit/attributions must be retained. + + 4. If the software is being supplied in source code form, but it is + derived or inspired from this source code, then Credit/Attribution + below must be provided in the source code near the top of the source + file. + + 5. The additional permissions listed here are required to be included + in their entirety with any license file of any derivative works that + use the above features. + +Credit/Attribution: + +This software uses the following components from Jetty Firmware: +(LIST OF COMPONENTS GOES HERE) +authored by Dan Newman and Jetty. + +-------- + + + # Preamble: GPLv3, etc. -- INTRODUCTION diff --git a/firmware/src/shared/StepperAccel.cc b/firmware/src/shared/StepperAccel.cc index ef7e42f..e5c9f2e 100644 --- a/firmware/src/shared/StepperAccel.cc +++ b/firmware/src/shared/StepperAccel.cc @@ -16,10 +16,15 @@ You should have received a copy of the GNU General Public License along with Grbl. If not, see . -*/ -/* The timer calculations of this module informed by the 'RepRap cartesian firmware' by Zack Smith - and Philipp Tiefenbacher. */ + The timer calculations of this module informed by the 'RepRap cartesian firmware' by Zack Smith + and Philipp Tiefenbacher. + + This module has been heavily modified from the original Marlin (https://github.com/ErikZalm). + JKN Advance, YAJ (Yet Another Jerk), Advance Pressure Relax and modifications originate from + Jetty Firmware (https://github.com/jetty840/G3Firmware). These modifications and features are + copyrighted and authored by Dan Newman and Jetty under GPL. Copyright (c) 2012. +*/ #include "Configuration.hh" diff --git a/firmware/src/shared/StepperAccel.hh b/firmware/src/shared/StepperAccel.hh index 6da057a..275be91 100644 --- a/firmware/src/shared/StepperAccel.hh +++ b/firmware/src/shared/StepperAccel.hh @@ -16,6 +16,11 @@ You should have received a copy of the GNU General Public License along with Grbl. If not, see . + + This module has been heavily modified from the original Marlin (https://github.com/ErikZalm). + JKN Advance, YAJ (Yet Another Jerk), Advance Pressure Relax and modifications originate from + Jetty Firmware (https://github.com/jetty840/G3Firmware). These modifications and features are + copyrighted and authored by Dan Newman and Jetty under GPL. Copyright (c) 2012. */ #ifndef STEPPERACCEL_HH diff --git a/firmware/src/shared/StepperAccelPlanner.cc b/firmware/src/shared/StepperAccelPlanner.cc index 48b1afa..1a0011b 100644 --- a/firmware/src/shared/StepperAccelPlanner.cc +++ b/firmware/src/shared/StepperAccelPlanner.cc @@ -16,9 +16,14 @@ You should have received a copy of the GNU General Public License along with Grbl. If not, see . -*/ -/* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */ + The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. + + This module has been heavily modified from the original Marlin (https://github.com/ErikZalm). + JKN Advance, YAJ (Yet Another Jerk), Advance Pressure Relax and modifications originate from + Jetty Firmware (https://github.com/jetty840/G3Firmware). These modifications and features are + copyrighted and authored by Dan Newman and Jetty under GPL. Copyright (c) 2012. +*/ /* Reasoning behind the mathematics in this module (in the key of 'Mathematica'): diff --git a/firmware/src/shared/StepperAccelPlanner.hh b/firmware/src/shared/StepperAccelPlanner.hh index 3aaec78..b87d951 100644 --- a/firmware/src/shared/StepperAccelPlanner.hh +++ b/firmware/src/shared/StepperAccelPlanner.hh @@ -16,6 +16,11 @@ You should have received a copy of the GNU General Public License along with Grbl. If not, see . + + This module has been heavily modified from the original Marlin (https://github.com/ErikZalm). + JKN Advance, YAJ (Yet Another Jerk), Advance Pressure Relax and modifications originate from + Jetty Firmware (https://github.com/jetty840/G3Firmware). These modifications and features are + copyrighted and authored by Dan Newman and Jetty under GPL. Copyright (c) 2012. */ // This module is to be considered a sub-module of stepper.c. Please don't include From c9dc34c3bd5d74f04ab8792f423433562adb965f Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 14 Sep 2012 19:49:38 -0600 Subject: [PATCH 52/61] Adding docs/avr-gcc.markdown --- docs/avr-gcc.markdown | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs/avr-gcc.markdown diff --git a/docs/avr-gcc.markdown b/docs/avr-gcc.markdown new file mode 100644 index 0000000..5d825e3 --- /dev/null +++ b/docs/avr-gcc.markdown @@ -0,0 +1,50 @@ +This firmware **REQUIRES** avr-gcc 4.6.2 or later. It will **NOT** function correctly when built with avr-gcc 4.5.x. While it is possible to compile it with 4.5.x, it will not function correctly. + +For directions on obtaining and building avr-gcc, please see [http://www.nongnu.org/avr-libc/user-manual/install_tools.html](). The following shell script illustrates how to build the tool chain. + + #!/bin/sh + + # Obtaining and building avr-gcc 4.6.3 + # + # For complete directions, see + # + # + + PREFIX=$HOME/local/avr + PATH=$PATH:$PREFIX/bin + + DIR=`pwd` + + curl -O http://ftp.gnu.org/gnu/binutils/binutils-2.22.tar.bz2 + bunzip2 -c binutils-2.22.tar.bz2 | tar xf - + cd binutils-2.22 + mkdir obj-avr + cd obj-avr + ../configure --prefix=$PREFIX --target=avr --disable-nls + make + make install + + cd $DIR + + curl -O http://mirrors-us.seosue.com/gcc/releases/gcc-4.6.3/gcc-core-4.6.3.tar.bz2 + curl -O http://mirrors-us.seosue.com/gcc/releases/gcc-4.6.3/gcc-g++-4.6.3.tar.bz2 + bunzip2 -c gcc-core-4.6.3.tar.bz2 | tar xf - + bunzip2 -c gcc-g++-4.6.3.tar.bz2 | tar xf - + cd gcc-4.6.3 + mkdir obj-avr + cd obj-avr + ../configure --prefix=$PREFIX --target=avr --enable-languages=c,c++ \ + --disable-nls --disable-libssp --with-dwarf2 + make + make install + + cd $DIR + + curl -O http://savannah.spinellicreations.com/avr-libc/avr-libc-1.8.0.tar.bz2 + bunzip2 -c avr-libc-1.8.0.tar.bz2 | tar xf - + cd avr-libc-1.8.0 + ./configure --prefix=$PREFIX --build=`./config.guess` --host=avr + make + make install + + cd $DIR From f8b0e404806c736388f40b9e5dbe0bc853539983 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Mon, 17 Sep 2012 12:05:45 -0600 Subject: [PATCH 53/61] Add python code to s3g.py to support new accelerated move s3g command --- tests/s3g_tests/G3FirmwareTests.py | 23 +++++++++++++++++++ tests/s3g_tests/G3Firmware_constants.py | 1 + .../G3Firmware_unsupported_functions.py | 1 + tests/s3g_tests/MightyBoard_constants.py | 3 ++- tests/s3g_tests/test_s3g_functions.py | 1 + 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/s3g_tests/G3FirmwareTests.py b/tests/s3g_tests/G3FirmwareTests.py index f4992d9..75b2011 100644 --- a/tests/s3g_tests/G3FirmwareTests.py +++ b/tests/s3g_tests/G3FirmwareTests.py @@ -11,6 +11,7 @@ import logging import tempfile import inspect +import math lib_path = os.path.abspath('./s3g') sys.path.append(lib_path) @@ -389,6 +390,28 @@ def test_SetGetExtendedPosition(self): self.r.set_extended_position(position) self.assertEqual(position, self.r.get_extended_position()[0]) + def test_QueuePointNewExt(self): + self.r.find_axes_maximums(constants['endstops_max'], 250, 60) + time.sleep(5) + self.r.find_axes_minimums(constants['endstops_min'], 250, 60) + time.sleep(5) + self.r.recall_home_positions(['x', 'y', 'z', 'a', 'b']) + self.r.set_extended_position([0, 0, 0, 0, 0]) + newPosition = constants['new_ext_point'] + # Distance in millimeters + distance = math.sqrt(pow(newPosition[0]/steps_per_mm[0],2) + pow(newPosition[1]/steps_per_mm[1],2) + pow(newPosition[2]/steps_per_mm[2],2)) + duration = 1.0 + # Feed rate in units of mm/s + feedRate = distance / duration + if feedRate > 150.0: + feedRate = 150.0 + # steps per second along the axis with the most steps + dda_rate = int(float(max([abs(newPosition[i]) for i in range(0,len(newPosition))])) / duration) + if dda_rate < 1: + dda_rate = 1 + self.r.queue_point_new_ext(newPosition, dda_rate, [], distance, feedRate); + time.sleep(int(duration + 1.0)) + def test_QueueExtendedPoint(self): self.r.find_axes_maximums(constants['endstops_max'], 500, 100) self.r.find_axes_minimums(constants['endstops_min'], 500, 100) diff --git a/tests/s3g_tests/G3Firmware_constants.py b/tests/s3g_tests/G3Firmware_constants.py index 721b0af..152361c 100644 --- a/tests/s3g_tests/G3Firmware_constants.py +++ b/tests/s3g_tests/G3Firmware_constants.py @@ -58,6 +58,7 @@ minimum speeds will cut in """ constants['new_ext_point'] = [500, 500, -10, 0, 0] +steps_per_mm = [47.069852, 47.069852, 200.0, 50.235478806907409, 50.235478806907409] # 1 for dual extruder, 0 for single extruder constants['dual_extruder'] = 0 diff --git a/tests/s3g_tests/G3Firmware_unsupported_functions.py b/tests/s3g_tests/G3Firmware_unsupported_functions.py index 3a43e15..1f56055 100644 --- a/tests/s3g_tests/G3Firmware_unsupported_functions.py +++ b/tests/s3g_tests/G3Firmware_unsupported_functions.py @@ -26,6 +26,7 @@ 'reset_to_factory', # 152 'build_start_notification', # 153 'build_end_notification', # 154 + 'queue_point_new_ext', # 155 # Tool Query Commands diff --git a/tests/s3g_tests/MightyBoard_constants.py b/tests/s3g_tests/MightyBoard_constants.py index 0e06acb..91d4b97 100644 --- a/tests/s3g_tests/MightyBoard_constants.py +++ b/tests/s3g_tests/MightyBoard_constants.py @@ -49,7 +49,8 @@ """ New point to move to """ -constants['new_ext_point'] = [-50, -50, 100, 0, 0] +constants['new_ext_point'] = [-500, -500, 100, 0, 0] +steps_per_mm = [94.139704, 94.139704, 400.0, 96.275201870333662468889989185642, 96.275201870333662468889989185642] # 1 for dual extruder, 0 for single extruder constants['dual_extruder'] = 1 diff --git a/tests/s3g_tests/test_s3g_functions.py b/tests/s3g_tests/test_s3g_functions.py index 9ba3d2e..7176dac 100644 --- a/tests/s3g_tests/test_s3g_functions.py +++ b/tests/s3g_tests/test_s3g_functions.py @@ -96,6 +96,7 @@ 'reset_to_factory' : [ (), 'time.sleep(5)' ], # 152 'build_start_notification' : [ ('aTest'), '%s.build_end_notification()' ], # 153 'build_end_notification' : (), # 154 + 'queue_point_new_ext' : ([0, 0, 0, 0, 0], 1, ['X', 'Y', 'Z', 'A', 'B'], 0.1, 30.0), # 155 # Tool Query Commands From 280e723db3f4aad3b4a37bd1908bcd742655222b Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Mon, 24 Sep 2012 11:28:58 -0600 Subject: [PATCH 54/61] Updating build file to produce warnings, link in -lm to save space and make finding the toolchain work. --- firmware/src/SConscript.motherboard | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/firmware/src/SConscript.motherboard b/firmware/src/SConscript.motherboard index 0822937..4cf37f2 100644 --- a/firmware/src/SConscript.motherboard +++ b/firmware/src/SConscript.motherboard @@ -139,7 +139,8 @@ flags=[ '-mmcu='+mcu, '-g', '-Os', - '-w', + '-Wall', + '-Winline', '-fno-exceptions', '-ffunction-sections', '-fdata-sections', @@ -155,7 +156,7 @@ if (os.environ.has_key('BUILD_NAME')): if (os.environ.has_key('AVR_TOOLS_PATH')): avr_tools_path = os.environ['AVR_TOOLS_PATH'] elif (os.environ.has_key('AVR32_HOME')): - avr_tools_path = os.environ['AVR32_HOME'] + '\\bin' + avr_tools_path = os.environ['AVR32_HOME'] + '/bin' else: lines = os.popen('/usr/bin/which avr-gcc').readlines() if(len(lines) > 0): @@ -183,7 +184,7 @@ hex_name = target_name + '.hex' elf_name = target_name + '.elf' map_name = target_name + '.map' -env.Append(BUILDERS={'Elf':Builder(action=avr_tools_path+"/avr-gcc -mmcu="+mcu+" -Os -Wl,--gc-sections -Wl,-Map,"+map_name+" -o $TARGET $SOURCES")}) +env.Append(BUILDERS={'Elf':Builder(action=avr_tools_path+"/avr-gcc -mmcu="+mcu+" -Os -Wl,--gc-sections -Wl,-Map,"+map_name+" -o $TARGET $SOURCES -lm")}) env.Append(BUILDERS={'Hex':Builder(action=avr_tools_path+"/avr-objcopy -O ihex -R .eeprom $SOURCES $TARGET")}) env.Elf(elf_name, objs) env.Hex(hex_name, elf_name) From 0b2e28ec3dbe4af61fdd661974f5bf6594a6576f Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Mon, 24 Sep 2012 11:33:36 -0600 Subject: [PATCH 55/61] Minor change to simulator reporting code --- firmware/src/shared/StepperAccelPlanner.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/src/shared/StepperAccelPlanner.cc b/firmware/src/shared/StepperAccelPlanner.cc index 1a0011b..9e4bd40 100644 --- a/firmware/src/shared/StepperAccelPlanner.cc +++ b/firmware/src/shared/StepperAccelPlanner.cc @@ -655,7 +655,8 @@ void calculate_trapezoid_for_block(block_t *block, FPTYPE entry_factor, FPTYPE e #endif } #ifdef SIMULATOR - if ( (advance_lead_entry < 0) || (advance_lead_exit < 0) || (advance_pressure_relax < 0) ) { + // Owing to roundoff errors, lead_entry and lead_exit can hit -1 and that's not abnormal + if ( (advance_lead_entry < -1) || (advance_lead_exit < -1) || (advance_pressure_relax < 0) ) { char buf[1024]; snprintf(buf, sizeof(buf), "*** calculate_trapezoid_for_block(): advance_lead_entry=%d, advance_lead_exit=%d, " From 73fa6e8a3c222ffe105c94b8a19353885a8fda19 Mon Sep 17 00:00:00 2001 From: dcnewman Date: Wed, 26 Sep 2012 15:29:27 -0700 Subject: [PATCH 56/61] Bump version number to 3.5 --- firmware/current_version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/current_version.txt b/firmware/current_version.txt index 8c50098..5a95802 100644 --- a/firmware/current_version.txt +++ b/firmware/current_version.txt @@ -1 +1 @@ -3.1 +3.5 From 1a78feba8e786d0ae76bc7e3a53a3717a5719cc1 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Thu, 27 Sep 2012 21:26:46 -0600 Subject: [PATCH 57/61] Bumping version to 3.5 --- firmware/current_version.txt | 2 +- firmware/src/shared/Menu.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/current_version.txt b/firmware/current_version.txt index 8c50098..5a95802 100644 --- a/firmware/current_version.txt +++ b/firmware/current_version.txt @@ -1 +1 @@ -3.1 +3.5 diff --git a/firmware/src/shared/Menu.cc b/firmware/src/shared/Menu.cc index a06421f..8171c40 100644 --- a/firmware/src/shared/Menu.cc +++ b/firmware/src/shared/Menu.cc @@ -308,7 +308,7 @@ int appendUint8(char *buf, uint8_t buflen, uint8_t val) void SplashScreen::update(LiquidCrystal& lcd, bool forceRedraw) { const static PROGMEM prog_uchar splash1[] = " Jetty Firmware "; const static PROGMEM prog_uchar splash2[] = " ------------- "; - const static PROGMEM prog_uchar splash3[] = "Thing 15380 3.4z"; + const static PROGMEM prog_uchar splash3[] = " Thing 15380 3.5"; const static PROGMEM prog_uchar splash4[] = " Revision: ____ "; if (forceRedraw) { From 1132500b22126c6b9d954d62411737deabf9f455 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Thu, 27 Sep 2012 21:27:29 -0600 Subject: [PATCH 58/61] ifdef'ing some things away when building simulator --- firmware/src/Motherboard/boards/mb24/Configuration.hh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firmware/src/Motherboard/boards/mb24/Configuration.hh b/firmware/src/Motherboard/boards/mb24/Configuration.hh index dbb8e81..bf09f92 100644 --- a/firmware/src/Motherboard/boards/mb24/Configuration.hh +++ b/firmware/src/Motherboard/boards/mb24/Configuration.hh @@ -18,6 +18,7 @@ #ifndef BOARDS_MB24_CONFIGURATION_HH_ #define BOARDS_MB24_CONFIGURATION_HH_ +#ifndef SIMULATOR /// This file details the pin assignments and features of the /// Makerbot Motherboard v2.x @@ -78,12 +79,14 @@ #define ESTOP_ENABLE_FALLING_INT { EICRB = 0x02; EIMSK |= 0x10; } #define ESTOP_vect INT4_vect +#endif // --- Axis configuration --- // Define the number of stepper axes supported by the board. The axes are // denoted by X, Y, Z, A and B. #define STEPPER_COUNT 5 +#ifndef SIMULATOR // --- Stepper and endstop configuration --- // Pins should be defined for each axis present on the board. They are denoted @@ -213,4 +216,6 @@ //WARNING: This probably will not fit on a 1280 (mb24). //#define EEPROM_MENU_ENABLE +#endif // #ifndef SIMULATOR + #endif // BOARDS_RRMBV12_CONFIGURATION_HH_ From 3c358c9600b03e3fe06db919735a060a379915a3 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Thu, 27 Sep 2012 21:28:20 -0600 Subject: [PATCH 59/61] Uping eeprom default Max FeedRates for X/Y Axis from 100 to 160 --- firmware/src/Motherboard/boards/mb24/EepromDefaults.hh | 4 ++-- .../src/Motherboard/boards/rrmbv12-2560/EepromDefaults.hh | 4 ++-- firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/firmware/src/Motherboard/boards/mb24/EepromDefaults.hh b/firmware/src/Motherboard/boards/mb24/EepromDefaults.hh index b0e3443..9e93be7 100644 --- a/firmware/src/Motherboard/boards/mb24/EepromDefaults.hh +++ b/firmware/src/Motherboard/boards/mb24/EepromDefaults.hh @@ -55,8 +55,8 @@ #define EEPROM_DEFAULT_STEPPER_DRIVER 0x03 -#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X 100 -#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X 160 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y 160 #define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Z 16 #define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_A 100 #define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_B 100 diff --git a/firmware/src/Motherboard/boards/rrmbv12-2560/EepromDefaults.hh b/firmware/src/Motherboard/boards/rrmbv12-2560/EepromDefaults.hh index df6b5af..6ad8775 100644 --- a/firmware/src/Motherboard/boards/rrmbv12-2560/EepromDefaults.hh +++ b/firmware/src/Motherboard/boards/rrmbv12-2560/EepromDefaults.hh @@ -33,8 +33,8 @@ #define EEPROM_DEFAULT_STEPPER_DRIVER 0x03 -#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X 100 -#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X 160 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y 160 #define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Z 8 #define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_A 100 #define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_B 100 diff --git a/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh b/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh index 556587e..eac8edb 100644 --- a/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh +++ b/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh @@ -36,8 +36,8 @@ #define EEPROM_DEFAULT_STEPPER_DRIVER 0x03 -#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X 100 -#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y 100 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_X 160 +#define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Y 160 #define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_Z 8 #define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_A 100 #define EEPROM_DEFAULT_ACCEL_MAX_FEEDRATE_B 100 From 49c6eab28dbdca8254263eac49526b2322b07810 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Fri, 12 Oct 2012 11:58:09 -0600 Subject: [PATCH 60/61] Defaulting cupcake ZHold to off --- firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh b/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh index eac8edb..fe57f57 100644 --- a/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh +++ b/firmware/src/Motherboard/boards/rrmbv12/EepromDefaults.hh @@ -1,7 +1,7 @@ // // MBI Firmware Defaults // -#define EEPROM_DEFAULT_AXIS_INVERSION (uint8_t)(1<<1) // Y axis = 1 +#define EEPROM_DEFAULT_AXIS_INVERSION (uint8_t)((1<<1) | (1<<7)) // Y axis = 1, ZHold Off #define EEPROM_DEFAULT_ENDSTOP_INVERSION (uint8_t)0b00011111 // All endstops inverted #define EEPROM_DEFAULT_MACHINE_NAME 0 // name is null From c30a75fb0858df366557805b01c82ff0a3f0a627 Mon Sep 17 00:00:00 2001 From: Jetty 840 Date: Sun, 25 Nov 2012 08:58:23 -0700 Subject: [PATCH 61/61] Minor change to make extruder firmware compile: needed a call argument added to eeprom_init() --- firmware/src/Extruder/EepromMap.cc | 2 +- firmware/src/Extruder/EepromMap.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/src/Extruder/EepromMap.cc b/firmware/src/Extruder/EepromMap.cc index 51e3eac..903894e 100644 --- a/firmware/src/Extruder/EepromMap.cc +++ b/firmware/src/Extruder/EepromMap.cc @@ -21,7 +21,7 @@ namespace eeprom { -void setDefaults() { +void setDefaults(bool dummy) { // Initialize eeprom map // Default: Heaters 0 and 1 enabled, thermistor on both. uint8_t features = eeprom::HEATER_0_PRESENT | diff --git a/firmware/src/Extruder/EepromMap.hh b/firmware/src/Extruder/EepromMap.hh index 036fc31..e300206 100644 --- a/firmware/src/Extruder/EepromMap.hh +++ b/firmware/src/Extruder/EepromMap.hh @@ -121,7 +121,7 @@ const static uint16_t THERM_TABLE_0 = 0x00f0; /// Thermistor table 1 const static uint16_t THERM_TABLE_1 = 0x0170; -void setDefaults(); +void setDefaults(bool dummy); } // namespace eeprom