From 0c77a2f5eb9986dfb2f13336537fe1786318e10b Mon Sep 17 00:00:00 2001 From: Adrian Bowyer Date: Wed, 29 May 2013 00:10:37 +0100 Subject: [PATCH] Started to add code to implement look-ahead. --- Configuration.h | 4 +- GCodes.h | 22 +++++++++-- GCodes.ino | 41 ++++++++++--------- Move.h | 75 +++++++++++++++++++++++++++++++++++ Move.ino | 98 +++++++++++++++++++++++++++++++++++++++++++--- RepRapFirmware.ino | 71 +++++++++++++++++++++++++++++++++ 6 files changed, 280 insertions(+), 31 deletions(-) diff --git a/Configuration.h b/Configuration.h index 66ac2f1..6465e4d 100644 --- a/Configuration.h +++ b/Configuration.h @@ -48,7 +48,6 @@ Licence: GPL #define CLIENT_CLOSE_DELAY 1000 // Microseconds to wait after serving a page #define INDEX_PAGE "reprap.htm" -#define PRINT_PAGE "print.php" #define MESSAGE_FILE "messages.txt" #define FOUR04_FILE "html404.htm" #define KO_START "rr_" @@ -62,4 +61,7 @@ Licence: GPL #define RING_LENGTH 10 +#define LOOK_AHEAD_LENGTH 25 +#define LOOK_AHEAD_FLUSH 5 +#define LOOK_AHEAD_FUTURE 15 #endif diff --git a/GCodes.h b/GCodes.h index 291cc86..ef41e7a 100644 --- a/GCodes.h +++ b/GCodes.h @@ -43,6 +43,8 @@ class GCodeBuffer int readPointer; }; +//**************************************************************************************************** + // The GCode interpreter class GCodes @@ -61,7 +63,7 @@ class GCodes boolean ActOnGcode(GCodeBuffer* gb); boolean SetUpMove(GCodeBuffer* gb); - boolean doDwell(GCodeBuffer *gb); + boolean DoDwell(GCodeBuffer *gb); Platform* platform; boolean active; Webserver* webserver; @@ -69,8 +71,8 @@ class GCodes boolean dwellWaiting; GCodeBuffer* webGCode; GCodeBuffer* fileGCode; - boolean webGCodePending; - boolean fileGCodePending; + boolean webGCodeFinished; + boolean fileGCodeFinished; boolean moveAvailable; boolean heatAvailable; float moveBuffer[DRIVES+1]; // Last is feedrate @@ -83,4 +85,18 @@ class GCodes int fileToPrint; }; +//***************************************************************************************************** + +// Get an Int after a G Code letter + +inline int GCodeBuffer::GetIValue() +{ + return (int)GetLValue(); +} + +inline char* GCodeBuffer::Buffer() +{ + return gcodeBuffer; +} + #endif diff --git a/GCodes.ino b/GCodes.ino index 4c43916..0a428a9 100644 --- a/GCodes.ino +++ b/GCodes.ino @@ -39,8 +39,8 @@ void GCodes::Init() { webGCode->Init(); fileGCode->Init(); - webGCodePending = false; - fileGCodePending = false; + webGCodeFinished = true; + fileGCodeFinished = true; active = true; moveAvailable = false; heatAvailable = false; @@ -61,22 +61,22 @@ void GCodes::Spin() if(!active) return; - if(webGCodePending) + if(!webGCodeFinished) { - webGCodePending = !ActOnGcode(webGCode); + webGCodeFinished = ActOnGcode(webGCode); return; } - if(fileGCodePending) + if(!fileGCodeFinished) { - fileGCodePending = !ActOnGcode(fileGCode); + fileGCodeFinished = ActOnGcode(fileGCode); return; } if(webserver->GCodeAvailable()) { if(webGCode->Put(webserver->ReadGCode())) - webGCodePending = !ActOnGcode(webGCode); + webGCodeFinished = ActOnGcode(webGCode); return; } @@ -86,9 +86,11 @@ void GCodes::Spin() if(platform->Read(fileBeingPrinted, b)) { if(fileGCode->Put(b)) - fileGCodePending = !ActOnGcode(fileGCode); + fileGCodeFinished = ActOnGcode(fileGCode); } else { + if(fileGCode->Put('\n')) // In case there wasn't one in the file + fileGCodeFinished = ActOnGcode(fileGCode); platform->Close(fileBeingPrinted); fileBeingPrinted = -1; } @@ -97,6 +99,8 @@ void GCodes::Spin() // Move expects all axis movements to be absolute, and all // extruder drive moves to be relative. This function serves that. +// If the Move class can't receive the move (i.e. things have to wait) +// this returns false, otherwise true. boolean GCodes::SetUpMove(GCodeBuffer *gb) { @@ -154,7 +158,7 @@ void GCodes::QueueFileToPrint(char* fileName) // Function to handle dwell delays. Return true for // Dwell finished, false otherwise. -boolean GCodes::doDwell(GCodeBuffer *gb) +boolean GCodes::DoDwell(GCodeBuffer *gb) { unsigned long dwell; @@ -187,6 +191,9 @@ boolean GCodes::doDwell(GCodeBuffer *gb) return false; } +// If the GCode to act on is completed, this returns true, +// otherwise false. + boolean GCodes::ActOnGcode(GCodeBuffer *gb) { int code; @@ -204,7 +211,7 @@ boolean GCodes::ActOnGcode(GCodeBuffer *gb) break; case 4: // Dwell - result = doDwell(gb); + result = DoDwell(gb); break; case 10: // Set offsets @@ -333,6 +340,8 @@ boolean GCodes::ActOnGcode(GCodeBuffer *gb) } } + // An empty buffer jumps straight here and gets disgarded + return result; } @@ -434,16 +443,6 @@ long GCodeBuffer::GetLValue() return result; } -// Get an Int after a G Code letter - -int GCodeBuffer::GetIValue() -{ - return (int)GetLValue(); -} - -char* GCodeBuffer::Buffer() -{ - return gcodeBuffer; -} + diff --git a/Move.h b/Move.h index 92a9af1..24ec6a7 100644 --- a/Move.h +++ b/Move.h @@ -62,6 +62,30 @@ class DDA volatile boolean active; }; +class LookAhead +{ + friend class Move; + + public: + LookAhead(Move* m, Platform* p, LookAhead* n); + LookAhead* Next(); + LookAhead* Previous(); + void Init(float m[], float uu, float vv); + void SetUV(float uu, float vv); + float* Movement(); + float U(); + float V(); + + private: + Move* move; + Platform* platform; + LookAhead* next; + LookAhead* previous; + float Cosine(LookAhead* a); + float movement[DRIVES+1]; // Last is feedrate + float u, v; +}; + class Move { @@ -87,6 +111,10 @@ class Move boolean DDARingFull(); boolean GetDDARingLock(); void ReleaseDDARingLock(); + boolean LookAheadRingEmpty(); + boolean LookAheadRingFull(); + boolean LookAheadRingAdd(float m[], float uu, float vv); + LookAhead* LookAheadRingGet(); Platform* platform; GCodes* gCodes; @@ -95,6 +123,10 @@ class Move DDA* ddaRingAddPointer; DDA* ddaRingGetPointer; volatile boolean ddaRingLocked; + + LookAhead* lookAheadRingAddPointer; + LookAhead* lookAheadRingGetPointer; + DDA* lookAheadDDA; unsigned long lastTime; boolean active; @@ -118,6 +150,37 @@ inline DDA* DDA::Next() return next; } +inline LookAhead* LookAhead::Next() +{ + return next; +} + +inline LookAhead* LookAhead::Previous() +{ + return previous; +} + +inline void LookAhead::SetUV(float uu, float vv) +{ + u = uu; + v = vv; +} + +inline float* LookAhead::Movement() +{ + return movement; +} + +inline float LookAhead::U() +{ + return u; +} + +inline float LookAhead::V() +{ + return v; +} + inline boolean Move::DDARingEmpty() { return ddaRingGetPointer == ddaRingAddPointer; @@ -130,6 +193,18 @@ inline boolean Move::DDARingFull() return ddaRingGetPointer->Next()->Next() == ddaRingAddPointer; } +inline boolean Move::LookAheadRingEmpty() +{ + return lookAheadRingGetPointer == lookAheadRingAddPointer; +} + +// Leave a gap of 2 as the last Get result may still be being processed + +inline boolean Move::LookAheadRingFull() +{ + return lookAheadRingGetPointer->Next()->Next() == lookAheadRingAddPointer; +} + inline boolean Move::GetDDARingLock() { if(ddaRingLocked) diff --git a/Move.ino b/Move.ino index a6b0dbe..62b6a27 100644 --- a/Move.ino +++ b/Move.ino @@ -22,6 +22,7 @@ Licence: GPL Move::Move(Platform* p, GCodes* g) { + char i; active = false; platform = p; gCodes = g; @@ -30,11 +31,28 @@ Move::Move(Platform* p, GCodes* g) ddaRingAddPointer = new DDA(this, platform, NULL); dda = ddaRingAddPointer; - for(char i = 1; i < RING_LENGTH; i++) + for(i = 1; i < RING_LENGTH; i++) dda = new DDA(this, platform, dda); ddaRingAddPointer->next = dda; dda = NULL; + + lookAheadRingAddPointer = new LookAhead(this, platform, NULL); + lookAheadRingGetPointer = lookAheadRingAddPointer; + for(i = 1; i < RING_LENGTH; i++) + lookAheadRingGetPointer = new LookAhead(this, platform, lookAheadRingGetPointer); + lookAheadRingAddPointer->next = lookAheadRingGetPointer; + + lookAheadRingGetPointer = lookAheadRingAddPointer; + for(i = 0; i < RING_LENGTH; i++) + { + lookAheadRingAddPointer = lookAheadRingAddPointer->Next(); + lookAheadRingAddPointer->previous = lookAheadRingGetPointer; + lookAheadRingGetPointer = lookAheadRingAddPointer; + } + + lookAheadDDA = new DDA(this, platform, NULL); + } void Move::Init() @@ -46,11 +64,13 @@ void Move::Init() for(i = 0; i <= AXES; i++) currentPosition[i] = 0.0; - // Empty the ring + // Empty the rings ddaRingGetPointer = ddaRingAddPointer; ddaRingLocked = false; + lookAheadRingGetPointer = lookAheadRingAddPointer; + // The stepDistances arrays are look-up tables of the Euclidean distance // between the start and end of a step. If the step is just along one axis, // it's just that axis's step length. If it's more, it is a Pythagoran @@ -227,6 +247,27 @@ void Move::Interrupt() dda = NULL; } +boolean Move::LookAheadRingAdd(float m[], float uu, float vv) +{ + if(LookAheadRingFull()) + return false; + lookAheadRingAddPointer->Init(m, uu, vv); + lookAheadRingAddPointer = lookAheadRingAddPointer->Next(); + return true; +} + + +LookAhead* Move::LookAheadRingGet() +{ + LookAhead* result; + if(LookAheadRingEmpty()) + return NULL; + result = lookAheadRingGetPointer; + lookAheadRingGetPointer = lookAheadRingGetPointer->Next(); + return result; +} + + boolean Move::AllMovesFinished() { // TODO - put some code in here @@ -240,16 +281,15 @@ boolean Move::AllMovesFinished() void Move::InterruptTime() { char buffer[50]; - DDA* ddax = new DDA(this, platform, NULL); float a[] = {1.0, 2.0, 3.0, 4.0, 5.0}; float b[] = {2.0, 3.0, 4.0, 5.0, 6.0}; float u = 50; float v = 50; - ddax->Init(a, b, u, v); - ddax->Start(false); + lookAheadDDA->Init(a, b, u, v); + lookAheadDDA->Start(false); unsigned long t = platform->Time(); for(long i = 0; i < 100000; i++) - ddax->Step(false); + lookAheadDDA->Step(false); t = platform->Time() - t; platform->Message(HOST_MESSAGE, "Time for 100000 calls of the interrupt function: "); sprintf(buffer, "%ld", t); @@ -552,5 +592,51 @@ void DDA::Step(boolean noTest) platform->SetInterrupt(STANDBY_INTERRUPT_RATE); } +//*************************************************************************************************** + +LookAhead::LookAhead(Move* m, Platform* p, LookAhead* n) +{ + move = m; + platform = p; + next = n; +} + +void LookAhead::Init(float m[], float uu, float vv) +{ + u = uu; + v = vv; + for(char i = 0; i <= DRIVES; i++) + movement[i] = m[i]; +} + +// This returns the cosine of the angle between +// the movement starting at start, and the movement +// starting at the end of that. Note that it +// includes Z movements, though Z values will almost always +// not change. + +float LookAhead::Cosine(LookAhead* start) +{ + LookAhead* n1 = start->Next(); + LookAhead* n2 = n1->Next(); + float sum = 0.0; + float a2 = 0.0; + float b2 = 0.0; + float m1; + float m2; + for(char i = 0; i < AXES; i++) + { + m1 = n1->movement[i] - start->movement[i]; + m2 = n2->movement[i] - n1->movement[i]; + a2 += m1*m1; + b2 += m2*m2; + sum += m1*m2; + } + sum = sum/( (float)sqrt(a2) * (float)sqrt(b2) ); + return sum; +} + + + diff --git a/RepRapFirmware.ino b/RepRapFirmware.ino index 58bcc8d..17ca63c 100644 --- a/RepRapFirmware.ino +++ b/RepRapFirmware.ino @@ -7,6 +7,7 @@ RepRap self-replicating 3D printers. It owes a lot to Marlin and to the original RepRap FiveD_GCode. + General design principles: * Control by RepRap G Codes. These are taken to be machine independent, though some may be unsupported. @@ -23,6 +24,76 @@ General design principles: * Don't avoid arrays and structs/classes, * Don't avoid pointers, * Use operator and function overloading where appropriate, particularly for vector algebra. + + +Naming conventions: + + * #defines are all capitals with optional underscores between words + * No underscores in other names - make readable with capitalisation: MakeReadableWithCapitalisation + * Classes and functions start with a capital letter + * Variables start with a lower case letter + + +Structure: + +There are six main classes: + + * RepRap + * GCodes + * Heat + * Move + * Platform, and + * Webserver + +RepRap: + +This is just a container class for the single instances of all the others, and otherwise does very little. + +GCodes: + +This class is fed GCodes, either from the web interface or from GCode files, interprests them, and requests +actions from the RepRap machine via the other classes. + +Heat: + +This class imlements all heating and temperature control in the RepRap machine. + +Move: + +This class controls all movement of the RepRap machine, both along its axes, and in its extruder drives. + +Platform: + +This is the only class that knows anything about the physical setup of the RepRap machine and its +controlling electronics. It implements the interface between all the other classes and the RepRap machine. +All the other classes are completely machine-independent (though they may declare arrays dimensioned +to values #defined in Platform.h). + +Webserver: + +This class talks to the network (via Platform) and implements a simple webserver to give an interactive +interface to the RepRap machine. It uses the Knockout and Jquery Javascript libraries to achieve this. + + + +When the software is running there is one single instance of each class, and all the memory allocation is +done on initialisation. new/malloc should not be used in the general running code, and delete is never +used. Each class has an Init() function that resets it to its boot-up state; the constructors merely handle +that memory allocation on startup. Calling RepRap.Init() calls all the other Init()s in the right sequence. + +There are other ancilliary classes that are declared in the .h files for the master classes that use them. For +example, Move has a DDA class that implements a Bresenham/digital differential analyser. + +All the main classes have a Spin() function. These are called in a loop by the RepRap.Spin() function and implement +simple timesharing. No class does, or ever should, wait inside one of its functions for anything to happen or call +any sort of delay() function. The general rule is: + + Can I do a thing? + Yes - do it + No - set a flag/timer to remind me to do it next-time-I'm-called/at-a-future-time and return. + +Note that it is simple to raise the "priority" of any class's activities relative to the others by calling its +Spin() function more than once from RepRap.Spin(). -----------------------------------------------------------------------------------------------------