Version 1.18alpha2

Initial implementation of babysteppnig
Fixed some issues with file upload and FTP networking on Duet Ethernet
(but FTP notr working yet)
Pass the name of the firmware file to IAP
Duet Ethernet and Duet 085 now each have their own Webserver modules
This commit is contained in:
David Crocker 2017-02-17 09:04:02 +00:00
parent 8e5c83a186
commit 7df2717ebd
33 changed files with 3625 additions and 11603 deletions

View file

@ -103,7 +103,6 @@
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Duet}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Duet/Lwip}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Duet/EMAC}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Webserver}&quot;"/>
</option>
<option id="gnu.cpp.compiler.option.preprocessor.def.1548770846" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols">
<listOptionValue builtIn="false" value="__SAM3X8E__"/>
@ -236,7 +235,7 @@
</toolChain>
</folderInfo>
<sourceEntries>
<entry excluding="src/Webserver|src/RADDS|src/DuetNG/DuetEthernet|src/Duet" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
<entry excluding="src/RADDS|src/DuetNG/DuetEthernet|src/Duet" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
</sourceEntries>
</configuration>
</storageModule>
@ -463,7 +462,6 @@
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/DuetNG}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/DuetNG/DuetEthernet}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/DuetNG/DuetEthernet/Wiznet/Ethernet}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Webserver}&quot;"/>
</option>
<option id="gnu.cpp.compiler.option.preprocessor.def.1120095540" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols">
<listOptionValue builtIn="false" value="__SAM4E8E__"/>
@ -508,21 +506,10 @@
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope" versionNumber="2">
<configuration configurationName="Release">
<resource resourceType="PROJECT" workspacePath="/RepRapFirmware"/>
</configuration>
<configuration configurationName="SAM3X_CoreNG"/>
<configuration configurationName="ReleaseWithCoreNG">
<resource resourceType="PROJECT" workspacePath="/RepRapFirmware"/>
</configuration>
<configuration configurationName="Duet085"/>
<configuration configurationName="SAM4E_CoreNG"/>
<configuration configurationName="DuetWiFi"/>
<configuration configurationName="ReleaseWithCoreDuet">
<resource resourceType="PROJECT" workspacePath="/RepRapFirmware"/>
</configuration>
<configuration configurationName="DuetEthernet"/>
<configuration configurationName="RADDS"/>
<configuration configurationName="DuetWiFi"/>
<configuration configurationName="Duet085"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
<storageModule moduleId="scannerConfiguration">

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -136,7 +136,6 @@ const float SILLY_Z_VALUE = -9999.0; // Millimetres
// String lengths
//const size_t LONG_STRING_LENGTH = 1024;
const size_t FORMAT_STRING_LENGTH = 256;
const size_t MACHINE_NAME_LENGTH = 40;
const size_t PASSWORD_LENGTH = 20;

View file

@ -94,7 +94,7 @@ a lot of data that needs to be copied, this should be set high. */
/* --------- IGMP options ---------- */
#define LWIP_IGMP 1
#define LWIP_IGMP 1 /* looks like this is needed for mdns support */
#ifdef __SAM3X8E__
# define LWIP_RAND trueRandom

View file

@ -651,13 +651,13 @@ void Webserver::HttpInterpreter::DoFastUpload()
platform->Message(GENERIC_MESSAGE, "Error: Could not write upload data!\n");
CancelUpload();
while (!network->Lock());
while (!network->Lock()) { }
SendJsonResponse("upload");
return;
}
}
}
while (!network->Lock());
while (!network->Lock()) { }
}
// See if the upload has finished
@ -2196,7 +2196,6 @@ void Webserver::FtpInterpreter::ProcessLine()
const uint8_t * const ip_address = network->GetIPAddress();
/* open random port > 1023 */
//rand(); // TRNG doesn't require this
uint16_t pasv_port = random(1024, 65535);
network->OpenDataPort(pasv_port);
portOpenTime = millis();

View file

@ -71,7 +71,7 @@ void Network::Spin(bool full)
usingDhcp = (ipAddress[0] == 0 && ipAddress[1] == 0 && ipAddress[2] == 0 && ipAddress[3] == 0);
if (usingDhcp)
{
debugPrintf("Link established, getting IP address\n");
// debugPrintf("Link established, getting IP address\n");
// IP address is all zeros, so use DHCP
DHCP_init(DhcpSocketNumber, hostname);
lastTickMillis = millis();
@ -79,7 +79,7 @@ void Network::Spin(bool full)
}
else
{
debugPrintf("Link established, network running\n");
// debugPrintf("Link established, network running\n");
InitSockets();
state = NetworkState::active;
}
@ -100,7 +100,7 @@ void Network::Spin(bool full)
const DhcpRunResult ret = DHCP_run();
if (ret == DhcpRunResult::DHCP_IP_ASSIGN)
{
debugPrintf("IP address obtained, network running\n");
// debugPrintf("IP address obtained, network running\n");
getSIPR(ipAddress);
// Send mDNS announcement so that some routers can perform hostname mapping
// if this board is connected via a non-IGMP capable WiFi bridge (like the TP-Link WR701N)
@ -111,7 +111,7 @@ void Network::Spin(bool full)
}
else
{
debugPrintf("Lost phy link\n");
// debugPrintf("Lost phy link\n");
DHCP_stop();
TerminateSockets();
state = NetworkState::establishingLink;
@ -135,7 +135,7 @@ void Network::Spin(bool full)
const DhcpRunResult ret = DHCP_run();
if (ret == DhcpRunResult::DHCP_IP_CHANGED)
{
debugPrintf("IP address changed\n");
// debugPrintf("IP address changed\n");
getSIPR(ipAddress);
}
}
@ -152,7 +152,7 @@ void Network::Spin(bool full)
}
else if (full)
{
debugPrintf("Lost phy link\n");
// debugPrintf("Lost phy link\n");
if (usingDhcp)
{
DHCP_stop();
@ -204,7 +204,7 @@ void Network::Stop()
{
DHCP_stop();
}
digitalWrite(EspResetPin, LOW); // put the ESP back into reset
digitalWrite(EspResetPin, LOW); // put the W5500 back into reset
state = NetworkState::disabled;
}
}
@ -334,34 +334,29 @@ NetworkTransaction *Network::GetTransaction(Connection conn)
void Network::OpenDataPort(Port port)
{
sockets[FtpSocketNumber].Init(FtpSocketNumber, port);
sockets[FtpSocketNumber].Poll(false);
sockets[FtpDataSocketNumber].Init(FtpDataSocketNumber, port);
sockets[FtpDataSocketNumber].Poll(false);
}
Port Network::GetDataPort() const
{
return sockets[FtpSocketNumber].GetLocalPort();
return sockets[FtpDataSocketNumber].GetLocalPort();
}
// Close FTP data port and purge associated PCB
void Network::CloseDataPort()
{
sockets[FtpSocketNumber].Close();
sockets[FtpDataSocketNumber].Close();
}
bool Network::AcquireFTPTransaction()
bool Network::AcquireTransaction(size_t socketNumber)
{
return sockets[FtpSocketNumber].AcquireTransaction();
}
bool Network::AcquireDataTransaction()
{
return sockets[FtpDataSocketNumber].AcquireTransaction();
}
bool Network::AcquireTelnetTransaction()
{
return sockets[TelnetSocketNumber].AcquireTransaction();
const bool success = sockets[socketNumber].AcquireTransaction();
// if (success)
// {
// currentTransactionSocketNumber = socketNumber;
// }
return success;
}
void Network::InitSockets()

View file

@ -65,9 +65,9 @@ public:
void SaveFTPConnection() {}
void SaveTelnetConnection() {}
bool AcquireFTPTransaction();
bool AcquireDataTransaction();
bool AcquireTelnetTransaction();
bool AcquireFTPTransaction() { return AcquireTransaction(FtpSocketNumber); }
bool AcquireDataTransaction() { return AcquireTransaction(FtpDataSocketNumber); }
bool AcquireTelnetTransaction() { return AcquireTransaction(TelnetSocketNumber); }
void Defer(NetworkTransaction *tr);
@ -90,6 +90,7 @@ private:
void InitSockets();
void TerminateSockets();
bool AcquireTransaction(size_t socketNumber);
Platform *platform;
float longWait;

View file

@ -160,7 +160,7 @@ void NetworkTransaction::SetFileToWrite(FileStore *file)
}
else if (file != nullptr)
{
debugPrintf("Want to write file but can't write\n");
// debugPrintf("Want to write file but can't write\n");
file->Close();
}
}

View file

@ -147,10 +147,17 @@ bool Socket::ReadBuffer(const char *&buffer, size_t &len)
// Poll a socket to see if it needs to be serviced
void Socket::Poll(bool full)
{
// Recycle any receive buffers that are now empty
while (receivedData != nullptr && receivedData->IsEmpty())
// The mechanism used by class OutputBuffer and now by NetworkBuffer of marking data taken as soon as we return a pointer to it
// is DANGEROUS and will have to be rewritten for RTOS. We need to recycle empty buffers, otherwise multiple file uploads get stalled.
// However, we MUST NOT do this until the data had DEFINITELY been finished with. Temporarily use this conditional to avoid a bug
// with data corruption when this is not the case.
if (full)
{
receivedData = receivedData->Release(); // discard empty buffer at head of chain
// Recycle any receive buffers that are now empty
while (receivedData != nullptr && receivedData->IsEmpty())
{
receivedData = receivedData->Release(); // discard empty buffer at head of chain
}
}
switch(getSn_SR(socketNum))
@ -193,7 +200,7 @@ void Socket::Poll(bool full)
else
{
// This should not happen
debugPrintf("ERROR:currentTransation should be null but isn't\n");
// debugPrintf("ERROR:currentTransation should be null but isn't\n");
}
}
state = SocketState::connected;
@ -374,7 +381,7 @@ void Socket::DiscardReceivedData()
}
}
// The webserver calls this to tell the socket that it needs a transaction, e.g. for sending a Telnet os FTP response.
// The webserver calls this to tell the socket that it needs a transaction, e.g. for sending a Telnet or FTP response.
// An empty transaction will do.
// Return true if we can do it, false if the connection is closed or closing.
bool Socket::AcquireTransaction()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,386 @@
/****************************************************************************************************
RepRapFirmware - Webserver
This class serves a single-page web applications to the attached network. This page forms the user's
interface with the RepRap machine. This software interprests returned values from the page and uses it
to Generate G Codes, which it sends to the RepRap. It also collects values from the RepRap like
temperature and uses those to construct the web page.
The page itself - reprap.htm - uses Knockout.js and Jquery.js. See:
http://knockoutjs.com/
http://jquery.com/
-----------------------------------------------------------------------------------------------------
Version 0.2
10 May 2013
Adrian Bowyer
RepRap Professional Ltd
http://reprappro.com
Licence: GPL
****************************************************************************************************/
#ifndef WEBSERVER_H
#define WEBSERVER_H
#include "NetworkDefs.h"
#include "RepRapFirmware.h"
#include "MessageType.h"
#include "Storage/FileData.h"
/* Generic values */
const size_t gcodeBufferLength = 512; // size of our gcode ring buffer, preferably a power of 2
/* HTTP */
#define KO_START "rr_"
#define KO_FIRST 3
const uint16_t webMessageLength = TCP_MSS; // maximum length of the web message we accept after decoding
const size_t minHttpResponseSize = 768; // minimum number of bytes required for an HTTP response
const size_t maxCommandWords = 4; // max number of space-separated words in the command
const size_t maxQualKeys = 5; // max number of key/value pairs in the qualifier
const size_t maxHeaders = 16; // max number of key/value pairs in the headers
const size_t maxHttpSessions = 8; // maximum number of simultaneous HTTP sessions
const uint32_t httpSessionTimeout = 8000; // HTTP session timeout in milliseconds
/* FTP */
const uint16_t ftpMessageLength = 128; // maximum line length for incoming FTP commands
const uint32_t ftpPasvPortTimeout = 10000; // maximum time to wait for an FTP data connection in milliseconds
/* Telnet */
const uint32_t telnetSetupDuration = 4000; // ignore the first Telnet request within this duration (in ms)
class Webserver;
// List of protocols that can execute G-Codes
enum class WebSource
{
HTTP,
Telnet
};
// This is the abstract class for all supported protocols
// Any inherited class should implement a state machine to increase performance and reduce memory usage.
class ProtocolInterpreter
{
public:
ProtocolInterpreter(Platform *p, Webserver *ws, Network *n);
virtual ~ProtocolInterpreter() { } // to keep Eclipse happy
virtual void Diagnostics(MessageType mtype) = 0;
virtual void Spin();
virtual void ConnectionEstablished();
virtual void ConnectionLost(Connection conn /*const ConnectionState *cs*/) { }
virtual bool CanParseData();
virtual bool CharFromClient(const char c) = 0;
virtual void NoMoreDataAvailable();
virtual bool DoingFastUpload() const;
virtual void DoFastUpload();
void CancelUpload(); // may be called from ISR!
protected:
Platform *platform;
Webserver *webserver;
Network *network;
// Information for file uploading
enum UploadState
{
notUploading, // no upload in progress
uploadOK, // upload in progress, no error so far
uploadError // upload in progress but had error
};
UploadState uploadState;
FileData fileBeingUploaded;
char filenameBeingUploaded[FILENAME_LENGTH];
bool StartUpload(FileStore *file, const char *fileName);
bool IsUploading() const;
bool FinishUpload(uint32_t fileLength);
};
class Webserver
{
public:
friend class Platform;
friend class ProtocolInterpreter;
Webserver(Platform* p, Network *n);
void Init();
void Spin();
void Exit();
void Diagnostics(MessageType mtype);
bool GCodeAvailable(const WebSource source) const;
char ReadGCode(const WebSource source);
void HandleGCodeReply(const WebSource source, OutputBuffer *reply);
void HandleGCodeReply(const WebSource source, const char *reply);
uint32_t GetReplySeq() const;
// Returns the available G-Code buffer space of the HTTP interpreter (may be dropped in a future version)
uint16_t GetGCodeBufferSpace(const WebSource source) const;
void ConnectionLost(Connection conn /*const ConnectionState *cs*/);
void ConnectionError();
protected:
class HttpInterpreter : public ProtocolInterpreter
{
public:
HttpInterpreter(Platform *p, Webserver *ws, Network *n);
void Spin();
void Diagnostics(MessageType mtype) override;
void ConnectionLost(Connection conn /*const ConnectionState *cs*/) override;
bool CanParseData() override;
bool CharFromClient(const char c) override;
void NoMoreDataAvailable() override;
void ResetState();
void ResetSessions();
bool DoingFastUpload() const override;
void DoFastUpload();
bool GCodeAvailable() const;
char ReadGCode();
void HandleGCodeReply(OutputBuffer *reply);
void HandleGCodeReply(const char *reply);
uint16_t GetGCodeBufferSpace() const;
uint32_t GetReplySeq() const;
private:
// HTTP server state enumeration. The order is important, in particular xxxEsc1 must follow xxx, and xxxEsc2 must follow xxxEsc1.
// We assume that qualifier keys do not contain escapes, because none of ours needs to be encoded. If we are sent escapes in the key,
// it won't do any harm, but the key won't be recognised even if it would be valid were it decoded.
enum HttpState
{
doingCommandWord, // receiving a word in the first line of the HTTP request
doingFilename, // receiving the filename (second word in the command line)
doingFilenameEsc1, // received '%' in the filename (e.g. we are being asked for a filename with spaces in it)
doingFilenameEsc2, // received '%' and one hex digit in the filename
doingQualifierKey, // receiving a key name in the HTTP request
doingQualifierValue, // receiving a key value in the HTTP request
doingQualifierValueEsc1, // received '%' in the qualifier
doingQualifierValueEsc2, // received '%' and one hex digit in the qualifier
doingHeaderKey, // receiving a header key
expectingHeaderValue, // expecting a header value
doingHeaderValue, // receiving a header value
doingHeaderContinuation // received a newline after a header value
};
HttpState state;
struct KeyValueIndices
{
const char* key;
const char* value;
};
void SendFile(const char* nameOfFileToSend, bool isWebFile);
void SendGCodeReply();
void SendJsonResponse(const char* command);
void GetJsonResponse(const char* request, OutputBuffer *&response, bool& keepOpen);
bool ProcessMessage();
bool RejectMessage(const char* s, unsigned int code = 500);
// Buffers for processing HTTP input
char clientMessage[webMessageLength + 3]; // holds the command, qualifier, and headers
size_t clientPointer; // current index into clientMessage
char decodeChar;
const char* commandWords[maxCommandWords];
KeyValueIndices qualifiers[maxQualKeys + 1]; // offsets into clientQualifier of the key/value pairs, the +1 is needed so that values can contain nulls
KeyValueIndices headers[maxHeaders]; // offsets into clientHeader of the key/value pairs
size_t numCommandWords;
size_t numQualKeys; // number of qualifier keys we have found, <= maxQualKeys
size_t numHeaderKeys; // number of keys we have found, <= maxHeaders
// HTTP sessions
struct HttpSession
{
uint32_t ip;
uint32_t lastQueryTime;
bool isPostUploading;
uint16_t postPort;
};
HttpSession sessions[maxHttpSessions];
uint8_t numSessions;
uint8_t clientsServed;
bool Authenticate();
bool IsAuthenticated() const;
void UpdateAuthentication();
bool RemoveAuthentication();
const char* GetKeyValue(const char *key) const; // return the value of the specified key, or nullptr if not present
// Deal with incoming G-Codes
char gcodeBuffer[gcodeBufferLength];
uint16_t gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
void LoadGcodeBuffer(const char* gc);
void ProcessGcode(const char* gc);
void StoreGcodeData(const char* data, uint16_t len);
// Responses from GCodes class
uint32_t seq; // Sequence number for G-Code replies
OutputStack *gcodeReply;
// File uploads
uint32_t postFileLength, uploadedBytes; // How many POST bytes do we expect and how many have already been written?
time_t fileLastModified;
// Deferred requests (rr_fileinfo)
volatile Connection deferredRequestConnection; // Which connection expects a response for a deferred request?
char filenameBeingProcessed[FILENAME_LENGTH]; // The filename being processed (for rr_fileinfo)
void ProcessDeferredRequest();
};
HttpInterpreter *httpInterpreter;
class FtpInterpreter : public ProtocolInterpreter
{
public:
FtpInterpreter(Platform *p, Webserver *ws, Network *n);
void Diagnostics(MessageType mtype) override;
void ConnectionEstablished() override;
void ConnectionLost(Connection conn /*const ConnectionState *cs*/) override;
bool CharFromClient(const char c) override;
void ResetState();
bool DoingFastUpload() const override;
private:
enum FtpState
{
idle, // no client connected
authenticating, // not logged in
authenticated, // logged in
waitingForPasvPort, // waiting for connection to be established on PASV port
pasvPortConnected, // client connected to PASV port, ready to send data
doingPasvIO // client is connected and data is being transferred
};
FtpState state;
uint8_t connectedClients;
char clientMessage[ftpMessageLength];
size_t clientPointer;
char filename[FILENAME_LENGTH];
char currentDir[FILENAME_LENGTH];
uint32_t portOpenTime;
void ProcessLine();
void SendReply(int code, const char *message, bool keepConnection = true);
void SendFeatures();
void ReadFilename(uint16_t start);
void ChangeDirectory(const char *newDirectory);
};
FtpInterpreter *ftpInterpreter;
class TelnetInterpreter : public ProtocolInterpreter
{
public:
TelnetInterpreter(Platform *p, Webserver *ws, Network *n);
void Diagnostics(MessageType mtype) override;
void ConnectionEstablished() override;
void ConnectionLost(Connection conn /*const ConnectionState *cs*/) override;
bool CanParseData() override;
bool CharFromClient(const char c) override;
void ResetState();
bool GCodeAvailable() const;
char ReadGCode();
void HandleGCodeReply(OutputBuffer *reply);
void HandleGCodeReply(const char *reply);
uint16_t GetGCodeBufferSpace() const;
void SendGCodeReply();
private:
enum TelnetState
{
idle, // not connected
justConnected, // not logged in, but the client has just connected
authenticating, // not logged in
authenticated // logged in
};
TelnetState state;
uint8_t connectedClients;
uint32_t connectTime;
bool processNextLine;
char clientMessage[GCODE_LENGTH];
size_t clientPointer;
bool ProcessLine();
// Deal with incoming G-Codes
char gcodeBuffer[gcodeBufferLength];
uint16_t gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
void ProcessGcode(const char* gc);
void StoreGcodeData(const char* data, uint16_t len);
// Converted response from GCodes class (NL -> CRNL)
OutputBuffer * volatile gcodeReply;
};
TelnetInterpreter *telnetInterpreter;
private:
Platform* platform;
Network* network;
bool webserverActive;
NetworkTransaction *currentTransaction;
volatile Connection readingConnection;
float longWait;
};
inline bool ProtocolInterpreter::CanParseData() { return true; }
inline bool ProtocolInterpreter::DoingFastUpload() const { return false; }
inline bool ProtocolInterpreter::IsUploading() const { return uploadState != notUploading; }
inline uint32_t Webserver::GetReplySeq() const { return httpInterpreter->GetReplySeq(); }
inline uint16_t Webserver::HttpInterpreter::GetGCodeBufferSpace() const { return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength; }
inline bool Webserver::HttpInterpreter::GCodeAvailable() const { return gcodeReadIndex != gcodeWriteIndex; }
inline uint32_t Webserver::HttpInterpreter::GetReplySeq() const { return seq; }
inline uint16_t Webserver::TelnetInterpreter::GetGCodeBufferSpace() const { return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength; }
inline bool Webserver::TelnetInterpreter::GCodeAvailable() const { return gcodeReadIndex != gcodeWriteIndex; }
#endif

View file

@ -234,9 +234,6 @@ namespace WizSpi
#endif
}
// The remaining functions are speed-critical, so use full optimisation
#pragma GCC optimize ("O3")
// Wait for transmit buffer empty, returning true if timed out
static inline bool waitForTxReady()
{

View file

@ -8,7 +8,7 @@
#include "RepRapFirmware.h"
#include "TMC2660.h"
#if !defined(PROTOTYPE_1)
const float MaximumMotorCurrent = 2500.0;
static size_t numTmc2660Drivers;
@ -239,50 +239,16 @@ void TmcDriverState::SetMicrostepping(uint32_t shift, bool interpolate)
// Set the motor current
void TmcDriverState::SetCurrent(float current)
{
#if defined(DUET_NG) && !defined(PROTOTYPE_1)
// The current sense resistor on the production Duet WiFi is 0.051 ohms.
// This gives us a range of 101mA to 3.236A in 101mA steps in the high sensitivity range (VSENSE = 1)
drvConfReg |= TMC_DRVCONF_VSENSE; // this should always be set, but send it again just in case
SpiSendWord(drvConfReg);
const uint32_t iCurrent = static_cast<uint32_t>(constrain<float>(current, 100.0, 2000.0));
const uint32_t iCurrent = static_cast<uint32_t>(constrain<float>(current, 100.0, MaximumMotorCurrent));
const uint32_t csBits = (32 * iCurrent - 1600)/3236; // formula checked by simulation on a spreadsheet
sgcsConfReg &= ~TMC_SGCSCONF_CS_MASK;
sgcsConfReg |= TMC_SGCSCONF_CS(csBits);
SpiSendWord(sgcsConfReg);
#else
// The current sense resistor is 0.1 ohms on the evaluation board.
// This gives us a range of 95mA to 3.05A in 95mA steps when VSENSE is low (but max allowed RMS current is 2A),
// or 52mA to 1.65A in 52mA steps when VSENSE is high.
if (current > 1650.0)
{
// Need VSENSE = 0, but set up the current first to avoid temporarily exceeding the 2A rating
const uint32_t iCurrent = (current > 2000.0) ? 2000 : (uint32_t)current;
const uint32_t csBits = (32 * iCurrent - 1500)/3050; // formula checked by simulation on a spreadsheet
sgcsConfReg &= ~TMC_SGCSCONF_CS_MASK;
sgcsConfReg |= TMC_SGCSCONF_CS(csBits);
SpiSendWord(sgcsConfReg);
drvConfReg &= ~TMC_DRVCONF_VSENSE;
SpiSendWord(drvConfReg);
}
else
{
// Use VSENSE = 1
drvConfReg |= TMC_DRVCONF_VSENSE;
SpiSendWord(drvConfReg);
const uint32_t iCurrent = (current < 50) ? 50 : (uint32_t)current;
const uint32_t csBits = (32 * iCurrent - 800)/1650; // formula checked by simulation on a spreadsheet
sgcsConfReg &= ~TMC_SGCSCONF_CS_MASK;
sgcsConfReg |= TMC_SGCSCONF_CS(csBits);
SpiSendWord(sgcsConfReg);
}
#endif
}
// Enable or disable the driver
@ -460,8 +426,6 @@ namespace TMC2660
}; // end namespace
#endif
// End

View file

@ -134,7 +134,7 @@ bool GCodeBuffer::Put(char c)
gcodeBuffer[gcodePointer++] = c;
if (gcodePointer >= (int)GCODE_LENGTH)
{
reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Error: G-Code buffer length overflow.\n");
reprap.GetPlatform()->MessageF(GENERIC_MESSAGE, "Error: G-Code buffer '$s' length overflow\n", identity);
gcodePointer = 0;
gcodeBuffer[0] = 0;
}

View file

@ -151,6 +151,7 @@ void GCodes::Reset()
toolChangeRestorePoint.Init();
ClearMove();
ClearBabyStepping();
for (size_t i = 0; i < MaxTriggers; ++i)
{
@ -173,6 +174,11 @@ void GCodes::Reset()
}
}
void GCodes::ClearBabyStepping()
{
pendingBabyStepZOffset = currentBabyStepZOffset = 0.0;
}
bool GCodes::DoingFileMacro() const
{
return fileGCode->IsDoingFileMacro();
@ -756,14 +762,14 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply)
gb.Init(); // mark buffer as empty
// Don't close the file until all moves have been completed, in case the print gets paused.
// Also, this keeps the state as 'Printing' until the print really has finished.
if (LockMovementAndWaitForStandstill(gb))
if (gb.MachineState().previous == nullptr)
{
fd.Close();
if (gb.MachineState().previous == nullptr)
// Finished printing SD card file
// Don't close the file until all moves have been completed, in case the print gets paused.
// Also, this keeps the state as 'Printing' until the print really has finished.
if (LockMovementAndWaitForStandstill(gb))
{
// Finished printing SD card file
fd.Close();
UnlockAll(gb);
reprap.GetPrintMonitor()->StoppedPrint();
if (platform->Emulating() == marlin)
@ -772,21 +778,22 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply)
HandleReply(gb, false, "Done printing file");
}
}
else
}
else
{
// Finished a macro or finished processing config.g
fd.Close();
if (runningConfigFile)
{
// Finished a macro or finished processing config.g
if (runningConfigFile)
{
CopyConfigFinalValues(gb);
runningConfigFile = false;
}
Pop(gb);
gb.Init();
if (gb.GetState() == GCodeState::normal)
{
UnlockAll(gb);
HandleReply(gb, false, "");
}
CopyConfigFinalValues(gb);
runningConfigFile = false;
}
Pop(gb);
gb.Init();
if (gb.GetState() == GCodeState::normal)
{
UnlockAll(gb);
HandleReply(gb, false, "");
}
}
return;
@ -953,9 +960,6 @@ bool GCodes::LockMovementAndWaitForStandstill(const GCodeBuffer& gb)
return false;
}
// Allow movement again
reprap.GetMove()->ResumeMoving();
// Get the current positions. These may not be the same as the ones we remembered from last time if we just did a special move.
reprap.GetMove()->GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes());
memcpy(moveBuffer.initialCoords, moveBuffer.coords, numAxes * sizeof(moveBuffer.initialCoords[0]));
@ -1142,6 +1146,7 @@ unsigned int GCodes::LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType)
}
else if (moveType == 0)
{
moveArg += currentBabyStepZOffset;
if (axis == Z_AXIS && isRetracted)
{
moveArg += retractHop; // handle firmware retraction on layer change
@ -1351,7 +1356,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
}
else
{
moveBuffer.coords[Z_AXIS] = zParam;
moveBuffer.coords[Z_AXIS] = zParam + currentBabyStepZOffset + retractHop; // handle firmware retraction on layer change
if (currentTool != nullptr)
{
moveBuffer.coords[Z_AXIS] -= currentTool->GetOffset()[Z_AXIS];
@ -1448,6 +1453,7 @@ bool GCodes::ReadMove(RawMove& m)
}
m = moveBuffer;
if (segmentsLeft == 1)
{
// If there is just 1 segment left, it doesn't matter if it is an arc move or not, just move to the end position
@ -1496,6 +1502,31 @@ bool GCodes::ReadMove(RawMove& m)
--segmentsLeft;
}
// Check for pending baby stepping
if (m.moveType == 0 && pendingBabyStepZOffset != 0.0)
{
// Calculate the move length, to see how much new babystepping is appropriate for this move
float xMoveLength = 0.0;
for (size_t drive = 0; drive < numAxes; ++drive)
{
if ((arcAxesMoving & (1 << drive)) != 0)
{
xMoveLength = max<float>(xMoveLength, fabs(m.coords[drive] - m.initialCoords[drive]));
}
}
const float distance = sqrtf(fsquare(xMoveLength) + fsquare(m.coords[Y_AXIS] - m.initialCoords[Y_AXIS]) + fsquare(m.coords[Z_AXIS] - m.initialCoords[Z_AXIS]));
// The maximum Z speed change due to baby stepping that we allow is the Z jerk rate, to avoid slowing the print down too much
const float minMoveTime = distance/m.feedRate;
const float maxBabyStepping = minMoveTime * platform->ConfiguredInstantDv(Z_AXIS);
const float babySteppingToDo = constrain<float>(pendingBabyStepZOffset, -maxBabyStepping, maxBabyStepping);
m.coords[Z_AXIS] += babySteppingToDo;
moveBuffer.initialCoords[Z_AXIS] = m.coords[Z_AXIS];
moveBuffer.coords[Z_AXIS] += babySteppingToDo;
pendingBabyStepZOffset -= babySteppingToDo;
currentBabyStepZOffset += babySteppingToDo;
}
return true;
}
@ -1603,9 +1634,9 @@ bool GCodes::SetPositions(GCodeBuffer& gb)
if (gb.Seen('Z'))
{
const float babystepAmount = gb.GetFValue() * distanceScale;
if (fabs(babystepAmount) <= 1.0) // limit babystepping to 1mm
if (fabs(babystepAmount) <= 1.0) // limit babystepping to 1mm
{
reprap.GetMove()->Babystep(babystepAmount);
pendingBabyStepZOffset += babystepAmount;
}
}
}
@ -1629,8 +1660,9 @@ bool GCodes::SetPositions(GCodeBuffer& gb)
{
return false;
}
ClearBabyStepping(); // G92 on any axis clears pending babystepping
}
else if (segmentsLeft != 0) // wait for previous move to be taken so that GetCurrentUserPosition returns the correct value
else if (segmentsLeft != 0) // wait for previous move to be taken so that GetCurrentUserPosition returns the correct value
{
return false;
}
@ -1748,6 +1780,7 @@ bool GCodes::DoHome(GCodeBuffer& gb, StringRef& reply, bool& error)
if (reprap.GetMove()->IsDeltaMode())
{
SetAllAxesNotHomed();
ClearBabyStepping();
DoFileMacro(gb, HOME_DELTA_G, true);
}
else
@ -1762,6 +1795,10 @@ bool GCodes::DoHome(GCodeBuffer& gb, StringRef& reply, bool& error)
}
}
if (toBeHomed == 0 || (toBeHomed & (1u << Z_AXIS)) != 0)
{
ClearBabyStepping();
}
if (toBeHomed == 0 || toBeHomed == ((1u << numAxes) - 1))
{
// Homing everything
@ -2537,7 +2574,6 @@ bool GCodes::DoDwellTime(float dwell)
if (platform->Time() - dwellTime >= 0.0)
{
dwellWaiting = false;
reprap.GetMove()->ResumeMoving();
return true;
}
return false;

View file

@ -225,10 +225,12 @@ private:
bool WriteConfigOverrideFile(StringRef& reply, const char *fileName) const; // Write the config-override file
void CopyConfigFinalValues(GCodeBuffer& gb); // Copy the feed rate etc. from the daemon to the input channels
void ClearBabyStepping();
static uint32_t LongArrayToBitMap(const long *arr, size_t numEntries); // Convert an array of longs to a bit map
Platform* const platform; // The RepRap machine
Webserver* const webserver; // The web server class
Platform* const platform; // The RepRap machine
Webserver* const webserver; // The web server class
GCodeBuffer* gcodeSources[6]; // The various sources of gcodes
@ -293,6 +295,8 @@ private:
float lastDefaultFanSpeed; // Last speed given in a M106 command with on fan number
float speedFactor; // speed factor, including the conversion from mm/min to mm/sec, normally 1/60
float extrusionFactors[MaxExtruders]; // extrusion factors (normally 1.0)
float currentBabyStepZOffset; // The accumulated Z offset due to baby stepping requests
float pendingBabyStepZOffset; // The amount of additional baby stepping requested but not yet acted on
// Z probe
float lastProbedZ; // the last height at which the Z probe stopped

View file

@ -214,6 +214,8 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
{
return false;
}
ClearBabyStepping();
if (reprap.GetMove()->IsDeltaMode() && !AllAxesAreHomed())
{
reply.copy("Must home a delta printer before bed probing");
@ -235,6 +237,8 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
return false;
}
ClearBabyStepping();
// Try to execute bed.g
if (!DoFileMacro(gb, BED_EQUATION_G, reprap.GetMove()->IsDeltaMode()))
{

View file

@ -259,7 +259,7 @@ bool DDA::Init(const GCodes::RawMove &nextMove, bool doMotorMapping)
{
if (delta > 0)
{
isPrintingMove = true; // we have both movement and extrusion
isPrintingMove = true; // we have both XY movement and extrusion
}
if (nextMove.usePressureAdvance)
{
@ -304,81 +304,6 @@ bool DDA::Init(const GCodes::RawMove &nextMove, bool doMotorMapping)
}
totalDistance = Normalise(directionVector, DRIVES, numAxes);
if (isDeltaMovement)
{
// The following are only needed when doing delta movements. We could defer computing them until Prepare(), which would make simulation faster.
a2plusb2 = fsquare(directionVector[X_AXIS]) + fsquare(directionVector[Y_AXIS]);
cKc = (int32_t)(directionVector[Z_AXIS] * DriveMovement::Kc);
const float initialX = prev->GetEndCoordinate(X_AXIS, false);
const float initialY = prev->GetEndCoordinate(Y_AXIS, false);
const DeltaParameters& dparams = move->GetDeltaParams();
const float diagonalSquared = fsquare(dparams.GetDiagonal());
const float a2b2D2 = a2plusb2 * diagonalSquared;
for (size_t drive = 0; drive < DELTA_AXES; ++drive)
{
const float A = initialX - dparams.GetTowerX(drive);
const float B = initialY - dparams.GetTowerY(drive);
const float stepsPerMm = reprap.GetPlatform()->DriveStepsPerUnit(drive);
DriveMovement& dm = ddm[drive];
const float aAplusbB = A * directionVector[X_AXIS] + B * directionVector[Y_AXIS];
const float dSquaredMinusAsquaredMinusBsquared = diagonalSquared - fsquare(A) - fsquare(B);
float h0MinusZ0 = sqrtf(dSquaredMinusAsquaredMinusBsquared);
dm.mp.delta.hmz0sK = (int32_t)(h0MinusZ0 * stepsPerMm * DriveMovement::K2);
dm.mp.delta.minusAaPlusBbTimesKs = -(int32_t)(aAplusbB * stepsPerMm * DriveMovement::K2);
dm.mp.delta.dSquaredMinusAsquaredMinusBsquaredTimesKsquaredSsquared =
(int64_t)(dSquaredMinusAsquaredMinusBsquared * fsquare(stepsPerMm * DriveMovement::K2));
// Calculate the distance at which we need to reverse direction.
if (a2plusb2 <= 0.0)
{
// Pure Z movement. We can't use the main calculation because it divides by a2plusb2.
dm.direction = (directionVector[Z_AXIS] >= 0.0);
dm.reverseStartStep = dm.totalSteps + 1;
}
else
{
// The distance to reversal is the solution to a quadratic equation. One root corresponds to the carriages being below the bed,
// the other root corresponds to the carriages being above the bed.
const float drev = ((directionVector[Z_AXIS] * sqrt(a2b2D2 - fsquare(A * directionVector[Y_AXIS] - B * directionVector[X_AXIS])))
- aAplusbB)/a2plusb2;
if (drev > 0.0 && drev < totalDistance) // if the reversal point is within range
{
// Calculate how many steps we need to move up before reversing
const float hrev = directionVector[Z_AXIS] * drev + sqrt(dSquaredMinusAsquaredMinusBsquared - 2 * drev * aAplusbB - a2plusb2 * fsquare(drev));
int32_t numStepsUp = (int32_t)((hrev - h0MinusZ0) * stepsPerMm);
// We may be almost at the peak height already, in which case we don't really have a reversal.
if (numStepsUp < 1 || (dm.direction && (uint32_t)numStepsUp <= dm.totalSteps))
{
dm.reverseStartStep = dm.totalSteps + 1;
}
else
{
dm.reverseStartStep = (uint32_t)numStepsUp + 1;
// Correct the initial direction and the total number of steps
if (dm.direction)
{
// Net movement is up, so we will go up a bit and then down by a lesser amount
dm.totalSteps = (2 * numStepsUp) - dm.totalSteps;
}
else
{
// Net movement is down, so we will go up first and then down by a greater amount
dm.direction = true;
dm.totalSteps = (2 * numStepsUp) + dm.totalSteps;
}
}
}
else
{
dm.reverseStartStep = dm.totalSteps + 1;
}
}
}
}
}
else
{
@ -424,7 +349,7 @@ bool DDA::Init(const GCodes::RawMove &nextMove, bool doMotorMapping)
// for diagonal moves. On a delta, this is not OK and any movement in the XY plane should be limited to the X/Y axis values, which we assume to be equal.
if (isDeltaMovement)
{
const float xyFactor = sqrtf(fsquare(normalisedDirectionVector[X_AXIS]) + fsquare(normalisedDirectionVector[X_AXIS]));
const float xyFactor = sqrtf(fsquare(normalisedDirectionVector[X_AXIS]) + fsquare(normalisedDirectionVector[Y_AXIS]));
const float maxSpeed = reprap.GetPlatform()->MaxFeedrates()[X_AXIS];
if (requestedSpeed * xyFactor > maxSpeed)
{
@ -766,15 +691,93 @@ float DDA::CalcTime() const
// This must not be called with interrupts disabled, because it calls Platform::EnableDrive.
void DDA::Prepare()
{
//debugPrintf("Prep\n");
if (isDeltaMovement)
{
// This code used to be in DDA::Init but we moved it here so that we can implement babystepping in it,
// also it speeds up simulation because we no longer execute this code when simulating.
// However, this code assumes that the previous move in the DDA ring is the previously-executed move, because it fetches the X and Y end coordinates from that move.
// Therefore the Move code must not store a new move in that entry until this one has been prepared! (It took me ages to track this down.)
// Ideally we would store the initial X any Y coordinates in the DDA, but we need to be economical with memory in the Duet 06/085 build.
a2plusb2 = fsquare(directionVector[X_AXIS]) + fsquare(directionVector[Y_AXIS]);
cKc = (int32_t)(directionVector[Z_AXIS] * DriveMovement::Kc);
const float initialX = prev->GetEndCoordinate(X_AXIS, false);
const float initialY = prev->GetEndCoordinate(Y_AXIS, false);
const DeltaParameters& dparams = reprap.GetMove()->GetDeltaParams();
const float diagonalSquared = fsquare(dparams.GetDiagonal());
const float a2b2D2 = a2plusb2 * diagonalSquared;
for (size_t drive = 0; drive < DELTA_AXES; ++drive)
{
const float A = initialX - dparams.GetTowerX(drive);
const float B = initialY - dparams.GetTowerY(drive);
const float stepsPerMm = reprap.GetPlatform()->DriveStepsPerUnit(drive);
DriveMovement& dm = ddm[drive];
const float aAplusbB = A * directionVector[X_AXIS] + B * directionVector[Y_AXIS];
const float dSquaredMinusAsquaredMinusBsquared = diagonalSquared - fsquare(A) - fsquare(B);
const float h0MinusZ0 = sqrtf(dSquaredMinusAsquaredMinusBsquared);
dm.mp.delta.hmz0sK = (int32_t)(h0MinusZ0 * stepsPerMm * DriveMovement::K2);
dm.mp.delta.minusAaPlusBbTimesKs = -(int32_t)(aAplusbB * stepsPerMm * DriveMovement::K2);
dm.mp.delta.dSquaredMinusAsquaredMinusBsquaredTimesKsquaredSsquared =
(int64_t)(dSquaredMinusAsquaredMinusBsquared * fsquare(stepsPerMm * DriveMovement::K2));
// Calculate the distance at which we need to reverse direction.
if (a2plusb2 <= 0.0)
{
// Pure Z movement. We can't use the main calculation because it divides by a2plusb2.
dm.direction = (directionVector[Z_AXIS] >= 0.0);
dm.reverseStartStep = dm.totalSteps + 1;
}
else
{
// The distance to reversal is the solution to a quadratic equation. One root corresponds to the carriages being below the bed,
// the other root corresponds to the carriages being above the bed.
const float drev = ((directionVector[Z_AXIS] * sqrt(a2b2D2 - fsquare(A * directionVector[Y_AXIS] - B * directionVector[X_AXIS])))
- aAplusbB)/a2plusb2;
if (drev > 0.0 && drev < totalDistance) // if the reversal point is within range
{
// Calculate how many steps we need to move up before reversing
const float hrev = directionVector[Z_AXIS] * drev + sqrt(dSquaredMinusAsquaredMinusBsquared - 2 * drev * aAplusbB - a2plusb2 * fsquare(drev));
const int32_t numStepsUp = (int32_t)((hrev - h0MinusZ0) * stepsPerMm);
// We may be almost at the peak height already, in which case we don't really have a reversal.
if (numStepsUp < 1 || (dm.direction && (uint32_t)numStepsUp <= dm.totalSteps))
{
dm.reverseStartStep = dm.totalSteps + 1;
}
else
{
dm.reverseStartStep = (uint32_t)numStepsUp + 1;
// Correct the initial direction and the total number of steps
if (dm.direction)
{
// Net movement is up, so we will go up a bit and then down by a lesser amount
dm.totalSteps = (2 * numStepsUp) - dm.totalSteps;
}
else
{
// Net movement is down, so we will go up first and then down by a greater amount
dm.direction = true;
dm.totalSteps = (2 * numStepsUp) + dm.totalSteps;
}
}
}
else
{
dm.reverseStartStep = dm.totalSteps + 1;
}
}
}
}
PrepParams params;
params.decelStartDistance = totalDistance - decelDistance;
// Convert the accelerate/decelerate distances to times
float accelStopTime = (topSpeed - startSpeed)/acceleration;
float decelStartTime = accelStopTime + (params.decelStartDistance - accelDistance)/topSpeed;
float totalTime = decelStartTime + (topSpeed - endSpeed)/acceleration;
const float accelStopTime = (topSpeed - startSpeed)/acceleration;
const float decelStartTime = accelStopTime + (params.decelStartDistance - accelDistance)/topSpeed;
const float totalTime = decelStartTime + (topSpeed - endSpeed)/acceleration;
clocksNeeded = (uint32_t)(totalTime * stepClockRate);

View file

@ -47,8 +47,6 @@ void Move::Init()
} while (dda != ddaRingAddPointer);
currentDda = nullptr;
addNoMoreMoves = false;
babysteppingLeft = 0.0;
stepErrors = 0;
numLookaheadUnderruns = numPrepareUnderruns = 0;
@ -145,9 +143,11 @@ void Move::Spin()
// See if we can add another move to the ring
if (
#if SUPPORT_ROLAND
!reprap.GetRoland()->Active() &&
!reprap.GetRoland()->Active() &&
#endif
!addNoMoreMoves && ddaRingAddPointer->GetState() == DDA::empty)
ddaRingAddPointer->GetState() == DDA::empty
&& ddaRingAddPointer->GetNext()->GetState() != DDA::provisional // function Prepare needs to access the endpoints in the previous move, so don't change them
)
{
// In order to react faster to speed and extrusion rate changes, only add more moves if the total duration of
// all un-frozen moves is less than 2 seconds, or the total duration of all but the first un-frozen move is
@ -389,13 +389,6 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui
return fPos;
}
// Request babystepping
void Move::Babystep(float zMovement)
{
babysteppingLeft += zMovement;
// TODO use this value somewhere
}
uint32_t maxReps = 0;
#if 0

View file

@ -53,7 +53,6 @@ public:
void Interrupt(); // The hardware's (i.e. platform's) interrupt should call this.
void InterruptTime(); // Test function - not used
bool AllMovesAreFinished(); // Is the look-ahead ring empty? Stops more moves being added as well.
void ResumeMoving(); // Allow moves to be added after a call to AllMovesAreFinished()
void DoLookAhead(); // Run the look-ahead procedure
void HitLowStop(size_t axis, DDA* hitDDA); // What to do when a low endstop is hit
void HitHighStop(size_t axis, DDA* hitDDA); // What to do when a high endstop is hit
@ -121,8 +120,6 @@ public:
HeightMap& AccessBedProbeGrid() { return grid; } // Access the bed probing grid
void Babystep(float zMovement); // Request babystepping
private:
enum class IdleState : uint8_t { idle, busy, timing };
@ -154,7 +151,6 @@ private:
DDA* volatile ddaRingGetPointer;
DDA* ddaRingCheckPointer;
bool addNoMoreMoves; // If true, allow no more moves to be added to the look-ahead
bool active; // Are we live and running?
uint8_t simulationMode; // Are we simulating, or really printing?
bool waitingForMove; // True if we are waiting for a new move
@ -198,8 +194,6 @@ private:
int coreXYMode; // 0 = Cartesian, 1 = CoreXY, 2 = CoreXZ, 3 = CoreYZ
float axisFactors[MAX_AXES]; // How much further the motors need to move for each axis movement, on a CoreXY/CoreXZ/CoreYZ machine
unsigned int stepErrors; // count of step errors, for diagnostics
float babysteppingLeft; // the amount of Z babystepping left to do
};
//******************************************************************************************************
@ -219,15 +213,9 @@ inline bool Move::NoLiveMovement() const
// Then call ResumeMoving() otherwise nothing more will ever happen.
inline bool Move::AllMovesAreFinished()
{
addNoMoreMoves = true;
return NoLiveMovement();
}
inline void Move::ResumeMoving()
{
addNoMoreMoves = false;
}
// Start the next move. Must be called with interrupts disabled, to avoid a race with the step ISR.
inline bool Move::StartNextMove(uint32_t startTime)
pre(ddaRingGetPointer->GetState() == DDA::frozen)

View file

@ -113,7 +113,7 @@ void setup()
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
// When doing a software reset, we disable the NRST input (User reset) to prevent the negative-going pulse that gets generated on it
// being held in the capacitor and changing the reset reason form Software to User. So enable it again here. We hope that the reset signal
// being held in the capacitor and changing the reset reason from Software to User. So enable it again here. We hope that the reset signal
// will have gone away by now.
#ifndef RSTC_MR_KEY_PASSWD
// Definition of RSTC_MR_KEY_PASSWD is missing in the SAM3X ASF files
@ -828,8 +828,6 @@ void Platform::UpdateFirmware()
#if (SAM4S || SAM4E)
// The EWP command is not supported for non-8KByte sectors in the SAM4 series.
// So we have to unlock and erase the complete 64Kb sector first.
// TODO save the NVRAM area and restore it later
flash_unlock(IAP_FLASH_START, IAP_FLASH_END, nullptr, nullptr);
flash_erase_sector(IAP_FLASH_START);
@ -936,7 +934,7 @@ void Platform::UpdateFirmware()
Message(FIRMWARE_UPDATE_MESSAGE, "Updating main firmware\n");
// Allow time for the firmware update message to be sent
uint32_t now = millis();
const uint32_t now = millis();
while (FlushMessages() && millis() - now < 2000) { }
// Step 2 - Let the firmware do whatever is necessary before we exit this program
@ -946,15 +944,29 @@ void Platform::UpdateFirmware()
// This does essentially what the Atmel AT02333 paper suggests (see 3.2.2 ff)
// Disable all IRQs
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk; // disable the system tick exception
cpu_irq_disable();
for(size_t i = 0; i < 8; i++)
for (size_t i = 0; i < 8; i++)
{
NVIC->ICER[i] = 0xFFFFFFFF; // Disable IRQs
NVIC->ICPR[i] = 0xFFFFFFFF; // Clear pending IRQs
NVIC->ICER[i] = 0xFFFFFFFF; // Disable IRQs
NVIC->ICPR[i] = 0xFFFFFFFF; // Clear pending IRQs
}
// Our SAM3X doesn't support disabling the watchdog, so leave it running.
// The IAP binary will kick it as soon as it's started
// Newer versions of iap4e.bin reserve space above the stack for us to pass the firmware filename
static const char filename[] = "0:/sys/" IAP_FIRMWARE_FILE;
const uint32_t topOfStack = *reinterpret_cast<uint32_t *>(IAP_FLASH_START);
if (topOfStack + sizeof(filename) <=
#if (SAM4S || SAM4E)
IRAM_ADDR + IRAM_SIZE
#else
IRAM1_ADDR + IRAM1_SIZE
#endif
)
{
memcpy(reinterpret_cast<char*>(topOfStack), filename, sizeof(filename));
}
wdt_restart(WDT); // kick the watchdog one last time
// Modify vector table location
__DSB();
@ -963,12 +975,13 @@ void Platform::UpdateFirmware()
__DSB();
__ISB();
// Reset stack pointer, enable IRQs again and start the new IAP binary
__set_MSP(*(uint32_t *)IAP_FLASH_START);
cpu_irq_enable();
void *entryPoint = (void *)(*(uint32_t *)(IAP_FLASH_START + 4));
goto *entryPoint;
__asm volatile ("mov r3, %0" : : "r" (IAP_FLASH_START) : "r3");
__asm volatile ("ldr sp, [r3]");
__asm volatile ("ldr r1, [r3, #4]");
__asm volatile ("orr r1, r1, #1");
__asm volatile ("bx r1");
}
// Send the beep command to the aux channel. There is no flow control on this port, so it can't block for long.
@ -1008,7 +1021,7 @@ float Platform::Time()
void Platform::Exit()
{
// Close all files
for(size_t i = 0; i < MAX_FILES; i++)
for (size_t i = 0; i < MAX_FILES; i++)
{
while (files[i]->inUse)
{
@ -1024,6 +1037,14 @@ void Platform::Exit()
// Stop processing data. Don't try to send a message because it will probably never get there.
active = false;
// Close down USB and serial ports
SERIAL_MAIN_DEVICE.end();
SERIAL_AUX_DEVICE.end();
#ifdef SERIAL_AUX2_DEVICE
SERIAL_AUX2_DEVICE.end();
#endif
}
Compatibility Platform::Emulating() const
@ -1455,13 +1476,19 @@ void Platform::Diagnostics(MessageType mtype)
Message(mtype, "Last software reset code ");
if (slot >= 0 && srdBuf[slot].magic == SoftwareResetData::magicValue)
{
scratchString.Clear();
for (size_t i = 0; i < ARRAY_SIZE(srdBuf[slot].stack); ++i)
// Our format buffer is only 256 characters long, so the next 2 lines must be written separately
MessageF(mtype, "0x%04x, HFSR 0x%08x, CFSR 0x%08x, ICSR 0x%08x, BFAR 0x%08x, SP 0x%08x\n",
srdBuf[slot].resetReason, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].icsr, srdBuf[slot].bfar, srdBuf[slot].sp);
if (srdBuf[slot].sp != 0xFFFFFFFF)
{
scratchString.catf(" %08x", srdBuf[slot].stack[i]);
// We saved a stack dump, so print it
scratchString.Clear();
for (size_t i = 0; i < ARRAY_SIZE(srdBuf[slot].stack); ++i)
{
scratchString.catf(" %08x", srdBuf[slot].stack[i]);
}
MessageF(mtype, "Stack:%s\n", scratchString.Pointer());
}
MessageF(mtype, "0x%04x, HFSR 0x%08x, CFSR 0x%08x, ICSR 0x%08x, BFAR 0x%08x, SP 0x%08x\nStack:%s\n",
srdBuf[slot].resetReason, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].icsr, srdBuf[slot].bfar, srdBuf[slot].sp, scratchString.Pointer());
MessageF(mtype, "Spinning module during software reset: %s, available RAM %u bytes (slot %d)\n",
moduleName[srdBuf[slot].resetReason & 0x0F], srdBuf[slot].neverUsedRam, slot);
}

View file

@ -167,7 +167,7 @@ enum class SoftwareResetReason : uint16_t
inUsbOutput = 0x4000 // this bit is or'ed in if we were in USB output at the time
};
// Enumeration to describe various tests we do in response to the M111 command
// Enumeration to describe various tests we do in response to the M122 command
enum class DiagnosticTestType : int
{
TestWatchdog = 1001, // test that we get a watchdog reset if the tick interrupt stops

View file

@ -5,7 +5,7 @@
const size_t NumFirmwareUpdateModules = 1;
#define IAP_UPDATE_FILE "iapradds.bin"
#define IAP_FIRMWARE_FILE "RepRapFirmware.bin"
#define IAP_FIRMWARE_FILE "RepRapFirmware-RADDS.bin"
// Default board type
#define DEFAULT_BOARD_TYPE BoardType::RADDS_15
@ -32,7 +32,7 @@ const int8_t HEATERS = 4;
// defaultPidKis[HEATERS] = {HEATERS_(5.0, 0.1, 0.1, 0.1, 0.1, 0.1)};
#define HEATERS_(a,b,c,d,e,f,g,h) { a,b,c,d }
const size_t MAX_AXES = 6; // FIXME The maximum number of movement axes in the machine, usually just X, Y and Z, <= DRIVES
const size_t MAX_AXES = 6; // The maximum number of movement axes in the machine, usually just X, Y and Z, <= DRIVES
const size_t MIN_AXES = 3; // The minimum and default number of axes
const size_t MaxExtruders = DRIVES - MIN_AXES; // The maximum number of extruders
@ -41,11 +41,10 @@ const size_t NUM_SERIAL_CHANNELS = 2;
#define SERIAL_MAIN_DEVICE SerialUSB
#define SERIAL_AUX_DEVICE Serial1
// The numbers of entries in each array must correspond with the values of DRIVES, AXES, or HEATERS. Set values to -1 to flag unavailability.
// The numbers of entries in each array must correspond with the values of DRIVES, AXES, or HEATERS. Set values to NoPin to flag unavailability.
// DRIVES
// X Y Z E1 E2 E3 E4 E5
const Pin ENABLE_PINS[DRIVES] = { 26, 22, 15, 62, 65, 49, 37, 31 };
const bool ENABLE_VALUES[DRIVES] = { false, false, false, false, false, false, false, false };
// A15 D04 B25 A02 B19 C12 C03 D06
const Pin STEP_PINS[DRIVES] = { 24, 17, 2, 61, 64, 51, 35, 29 };
const Pin DIRECTION_PINS[DRIVES] = { 23, 16, 3, 60, 63, 53, 33, 27 };
@ -110,7 +109,6 @@ const Pin Z_PROBE_PIN = 5; // RADDS "ADC" pin
const Pin Z_PROBE_MOD_PIN = 34;
// Use a PWM capable pin
// Firmware uses SamNonDue so feel free to use a non-Arduino pin
const size_t NUM_FANS = 2;
const Pin COOLING_FAN_PINS[NUM_FANS] = { 9, 8 }; // Fan 0, Fan 1

View file

@ -186,7 +186,7 @@ const char *moduleName[] =
// Utilities and storage not part of any class
static char scratchStringBuffer[150]; // this is now used only for short messages; needs to be long enough to print delta parameters and 12 words of stack (132 bytes)
static char scratchStringBuffer[170]; // this needs to be long enough to print delta parameters and 18 words of stack (162 bytes)
StringRef scratchString(scratchStringBuffer, ARRAY_SIZE(scratchStringBuffer));
// For debug use

View file

@ -490,28 +490,24 @@ unsigned int RepRap::GetNumberOfContiguousTools() const
void RepRap::Tick()
{
if (active)
if (active && !resetting)
{
Platform::KickWatchdog();
if (!resetting)
platform->Tick();
++ticksInSpinState;
if (ticksInSpinState >= 20000) // if we stall for 20 seconds, save diagnostic data and reset
{
platform->Tick();
++ticksInSpinState;
if (ticksInSpinState >= 20000) // if we stall for 20 seconds, save diagnostic data and reset
resetting = true;
for(size_t i = 0; i < HEATERS; i++)
{
resetting = true;
for(size_t i = 0; i < HEATERS; i++)
{
platform->SetHeater(i, 0.0);
}
for(size_t i = 0; i < DRIVES; i++)
{
platform->DisableDrive(i);
// We can't set motor currents to 0 here because that requires interrupts to be working, and we are in an ISR
}
platform->SoftwareReset((uint16_t)SoftwareResetReason::stuckInSpin);
platform->SetHeater(i, 0.0);
}
for(size_t i = 0; i < DRIVES; i++)
{
platform->DisableDrive(i);
// We can't set motor currents to 0 here because that requires interrupts to be working, and we are in an ISR
}
platform->SoftwareReset((uint16_t)SoftwareResetReason::stuckInSpin);
}
}
}

View file

@ -9,11 +9,11 @@
#define SRC_VERSION_H_
#ifndef VERSION
# define VERSION "1.17e"
# define VERSION "1.18alpha2"
#endif
#ifndef DATE
# define DATE "2017-02-10"
# define DATE "2017-02-17"
#endif
#define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman"