Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2020-08-28 12:25:34 +0300
committerDavid Crocker <dcrocker@eschertech.com>2020-08-28 12:25:34 +0300
commitfacd6fa3f9979b7fb8d7eec1839b3ad3dfa167e7 (patch)
treeb379c1d26ff220c52ad9129187c560c9fffa999b /src
parent05c995f2968aabaa3fa8582fb49ab0574e492342 (diff)
Various
Changed layout of User Page to accommodate ADC calibration as well as software reset data. Added new module NonVolatileMemory to manage this. Removed RADDS configuration so that we don't need to support SAM3XA flash memory. Increased number of stack words stored in software reset data. Moved exception handlers out of Tasks.cpp to separate module ExceptionHandlers. Changed number of decimal places reported in spindle RPMs from default (7) to 1. Moved some low-level functions from CanInterface to CanDriver and started preparing CanDriver to support bith CAN interfaces. Added L parameter (calibration factor) to laser filament monitor configuration Renamed GCodeChannel::USBchan back to USB for backwards compatibility
Diffstat (limited to 'src')
-rw-r--r--src/CAN/CanInterface.cpp122
-rw-r--r--src/Duet5LC/Pins_Duet5LC.h2
-rw-r--r--src/FilamentMonitors/LaserFilamentMonitor.cpp23
-rw-r--r--src/FilamentMonitors/LaserFilamentMonitor.h1
-rw-r--r--src/GCodes/GCodeChannel.h6
-rw-r--r--src/GCodes/GCodes.cpp4
-rw-r--r--src/GCodes/GCodes.h3
-rw-r--r--src/GCodes/GCodes2.cpp4
-rw-r--r--src/Hardware/ExceptionHandlers.cpp283
-rw-r--r--src/Hardware/ExceptionHandlers.h15
-rw-r--r--src/Hardware/NonVolatileMemory.cpp171
-rw-r--r--src/Hardware/NonVolatileMemory.h59
-rw-r--r--src/Hardware/SAME70/CanDriver.cpp91
-rw-r--r--src/Hardware/SAME70/CanDriver.h14
-rw-r--r--src/Hardware/SoftwareReset.cpp (renamed from src/SoftwareReset.cpp)7
-rw-r--r--src/Hardware/SoftwareReset.h (renamed from src/SoftwareReset.h)30
-rw-r--r--src/Heating/Sensors/Thermistor.cpp57
-rw-r--r--src/Linux/LinuxInterface.cpp4
-rw-r--r--src/Platform.cpp110
-rw-r--r--src/Platform.h1
-rw-r--r--src/RepRap.cpp130
-rw-r--r--src/RepRap.h2
-rw-r--r--src/Tasks.cpp211
-rw-r--r--src/Tools/Spindle.cpp6
-rw-r--r--src/Version.h2
25 files changed, 776 insertions, 582 deletions
diff --git a/src/CAN/CanInterface.cpp b/src/CAN/CanInterface.cpp
index 8d0eecc3..49f64a7e 100644
--- a/src/CAN/CanInterface.cpp
+++ b/src/CAN/CanInterface.cpp
@@ -30,8 +30,6 @@
#include <memory>
-static constexpr uint8_t dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64};
-
const unsigned int NumCanBuffers = 2 * MaxCanBoards + 10;
constexpr uint32_t MaxMotionSendWait = 20; // milliseconds
@@ -142,8 +140,6 @@ static Task<CanSenderTaskStackWords> canClockTask;
static CanMessageBuffer * volatile pendingBuffers;
static CanMessageBuffer * volatile lastBuffer; // only valid when pendingBuffers != nullptr
-static TaskHandle taskWaitingOnFifo[2] = { nullptr, nullptr };
-
static uint32_t messagesSent = 0;
static uint32_t numTxTimeouts = 0;
static uint32_t longestWaitTime = 0;
@@ -192,51 +188,6 @@ static void configure_mcan() noexcept
mcan_start(&mcan_instance);
}
-static void GetLocalCanTiming(CanTiming& timing) noexcept
-{
- const uint32_t nbtp = MCAN_MODULE->MCAN_NBTP;
- const uint32_t tseg1 = (nbtp & MCAN_NBTP_NTSEG1_Msk) >> MCAN_NBTP_NTSEG1_Pos;
- const uint32_t tseg2 = (nbtp & MCAN_NBTP_NTSEG2_Msk) >> MCAN_NBTP_NTSEG2_Pos;
- const uint32_t jw = (nbtp & MCAN_NBTP_NSJW_Msk) >> MCAN_NBTP_NSJW_Pos;
- const uint32_t brp = (nbtp & MCAN_NBTP_NBRP_Msk) >> MCAN_NBTP_NBRP_Pos;
- timing.period = (tseg1 + tseg2 + 3) * (brp + 1);
- timing.tseg1 = (tseg1 + 1) * (brp + 1);
- timing.jumpWidth = (jw + 1) * (brp + 1);
-}
-
-static void ChangeLocalCanTiming(const CanTiming& timing) noexcept
-{
- // Sort out the bit timing
- uint32_t period = timing.period;
- uint32_t tseg1 = timing.tseg1;
- uint32_t jumpWidth = timing.jumpWidth;
- uint32_t prescaler = 1; // 48MHz main clock
- uint32_t tseg2;
-
- for (;;)
- {
- tseg2 = period - tseg1 - 1;
- if (tseg1 <= 32 && tseg2 <= 16 && jumpWidth <= 16)
- {
- break;
- }
- prescaler <<= 1;
- period >>= 1;
- tseg1 >>= 1;
- jumpWidth >>= 1;
- }
-
- //TODO stop transmissions in an orderly fashion, or postpone initialising CAN until we have the timing data
- MCAN_MODULE->MCAN_CCCR |= MCAN_CCCR_CCE | MCAN_CCCR_INIT;
-
- MCAN_MODULE->MCAN_NBTP = ((tseg1 - 1) << MCAN_NBTP_NTSEG1_Pos)
- | ((tseg2 - 1) << MCAN_NBTP_NTSEG2_Pos)
- | ((jumpWidth - 1) << MCAN_NBTP_NSJW_Pos)
- | ((prescaler - 1) << MCAN_NBTP_NBRP_Pos);
-
- MCAN_MODULE->MCAN_CCCR &= ~MCAN_CCCR_CCE;
-}
-
extern "C" [[noreturn]] void CanSenderLoop(void *) noexcept;
extern "C" [[noreturn]] void CanClockLoop(void *) noexcept;
extern "C" [[noreturn]] void CanReceiverLoop(void *) noexcept;
@@ -302,17 +253,18 @@ void CanInterface::CheckCanAddress(uint32_t address, const GCodeBuffer& gb) THRO
}
// Send extended CAN message in FD mode
-static status_code mcan_fd_send_ext_message(uint32_t id_value, const uint8_t *data, size_t dataLength, uint32_t whichTxBuffer, uint32_t maxWait, bool bitRateSwitch) noexcept
+static status_code mcan_fd_send_ext_message(mcan_module *const module_inst, uint32_t id_value, const uint8_t *data, size_t dataLength, uint32_t whichTxBuffer, uint32_t maxWait, bool bitRateSwitch) noexcept
{
- if (WaitForTxBufferFree(&mcan_instance, whichTxBuffer, maxWait))
+ if (WaitForTxBufferFree(module_inst, whichTxBuffer, maxWait))
{
++numTxTimeouts;
}
++messagesSent;
- return mcan_fd_send_ext_message_no_wait(&mcan_instance, id_value, data, dataLength, whichTxBuffer, bitRateSwitch);
+ return mcan_fd_send_ext_message_no_wait(module_inst, id_value, data, dataLength, whichTxBuffer, bitRateSwitch);
}
// Interrupt handler for MCAN, including RX,TX,ERROR and so on processes
+//TODO move this to CanDriver
extern "C" void MCAN_INT0_Handler() noexcept
{
const uint32_t status = mcan_read_interrupt_status(&mcan_instance);
@@ -347,13 +299,13 @@ extern "C" void MCAN_INT0_Handler() noexcept
if (status & MCAN_RX_FIFO_0_NEW_MESSAGE)
{
mcan_clear_interrupt_status(&mcan_instance, MCAN_RX_FIFO_0_NEW_MESSAGE);
- TaskBase::GiveFromISR(taskWaitingOnFifo[0]);
+ TaskBase::GiveFromISR(mcan_instance.taskWaitingOnFifo[0]);
}
if (status & MCAN_RX_FIFO_1_NEW_MESSAGE)
{
mcan_clear_interrupt_status(&mcan_instance, MCAN_RX_FIFO_1_NEW_MESSAGE);
- TaskBase::GiveFromISR(taskWaitingOnFifo[1]);
+ TaskBase::GiveFromISR(mcan_instance.taskWaitingOnFifo[1]);
}
if ((status & MCAN_ACKNOWLEDGE_ERROR))
@@ -393,7 +345,7 @@ extern "C" [[noreturn]] void CanSenderLoop(void *) noexcept
CanMessageBuffer * const urgentMessage = CanMotion::GetUrgentMessage();
if (urgentMessage != nullptr)
{
- mcan_fd_send_ext_message(urgentMessage->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(urgentMessage->msg)), urgentMessage->dataLength, TxBufferIndexUrgent, MaxUrgentSendWait, false);
+ mcan_fd_send_ext_message(&mcan_instance, urgentMessage->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(urgentMessage->msg)), urgentMessage->dataLength, TxBufferIndexUrgent, MaxUrgentSendWait, false);
}
else if (pendingBuffers != nullptr)
{
@@ -408,7 +360,7 @@ extern "C" [[noreturn]] void CanSenderLoop(void *) noexcept
buf->msg.move.DebugPrint();
#endif
// Send the message
- mcan_fd_send_ext_message(buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferIndexMotion, MaxMotionSendWait, false);
+ mcan_fd_send_ext_message(&mcan_instance, buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferIndexMotion, MaxMotionSendWait, false);
#ifdef CAN_DEBUG
// Display a debug message too
@@ -635,60 +587,18 @@ void CanInterface::SendMotion(CanMessageBuffer *buf) noexcept
canSenderTask.Give();
}
-//TODO move this to CanDriver
-// Get a message from a FIFO with timeout
-// Return true if successful, false if we timed out
-bool GetMessageFromFifo(CanMessageBuffer *buf, unsigned int fifoNumber, uint32_t timeout) noexcept
-{
- volatile uint32_t* const fifoRegisters = &(mcan_instance.hw->MCAN_RXF0S) + (4 * fifoNumber); // pointer to FIFO status register followed by FIFO acknowledge register
- taskWaitingOnFifo[fifoNumber] = TaskBase::GetCallerTaskHandle();
- while (true)
- {
- const uint32_t status = fifoRegisters[0]; // get FIFO status
- if ((status & MCAN_RXF0S_F0FL_Msk) != 0) // if there are any messages
- {
- const uint32_t getIndex = (status & MCAN_RXF0S_F0GI_Msk) >> MCAN_RXF0S_F0GI_Pos;
- mcan_rx_element elem;
- if (fifoNumber == 1)
- {
- mcan_get_rx_fifo_1_element(&mcan_instance, &elem, getIndex); // copy the data (TODO use our own driver, avoid double copying)
- }
- else
- {
- mcan_get_rx_fifo_0_element(&mcan_instance, &elem, getIndex); // copy the data (TODO use our own driver, avoid double copying)
- }
- fifoRegisters[1] = getIndex; // acknowledge it, release the FIFO entry
-
- if (elem.R0.bit.XTD == 1 && elem.R0.bit.RTR != 1) // if extended address and not a remote frame
- {
- // Copy the message and accompanying data to our buffer
- buf->id.SetReceivedId(elem.R0.bit.ID);
- buf->dataLength = dlc2len[elem.R1.bit.DLC];
- memcpy(buf->msg.raw, elem.data, buf->dataLength);
- taskWaitingOnFifo[fifoNumber] = nullptr;
- return true;
- }
- }
- else if (!TaskBase::Take(timeout))
- {
- taskWaitingOnFifo[fifoNumber] = nullptr;
- return false;
- }
- }
-}
-
// Send a request to an expansion board and append the response to 'reply'
GCodeResult CanInterface::SendRequestAndGetStandardReply(CanMessageBuffer *buf, CanRequestId rid, const StringRef& reply, uint8_t *extra) noexcept
{
const CanAddress dest = buf->id.Dst();
- mcan_fd_send_ext_message(buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferIndexRequest, MaxRequestSendWait, false);
+ mcan_fd_send_ext_message(&mcan_instance, buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferIndexRequest, MaxRequestSendWait, false);
const uint32_t whenStartedWaiting = millis();
unsigned int fragmentsReceived = 0;
const CanMessageType msgType = buf->id.MsgType(); // save for possible error message
for (;;)
{
const uint32_t timeWaiting = millis() - whenStartedWaiting;
- if (!GetMessageFromFifo(buf, 1, CanResponseTimeout - timeWaiting))
+ if (!GetMessageFromFifo(&mcan_instance, buf, 1, CanResponseTimeout - timeWaiting))
{
break;
}
@@ -739,21 +649,21 @@ GCodeResult CanInterface::SendRequestAndGetStandardReply(CanMessageBuffer *buf,
// Send a response to an expansion board and free the buffer
void CanInterface::SendResponse(CanMessageBuffer *buf) noexcept
{
- mcan_fd_send_ext_message(buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferIndexResponse, MaxResponseSendWait, false);
+ mcan_fd_send_ext_message(&mcan_instance, buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferIndexResponse, MaxResponseSendWait, false);
CanMessageBuffer::Free(buf);
}
// Send a broadcast message and free the buffer
void CanInterface::SendBroadcast(CanMessageBuffer *buf) noexcept
{
- mcan_fd_send_ext_message(buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferBroadcast, MaxResponseSendWait, false);
+ mcan_fd_send_ext_message(&mcan_instance, buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferBroadcast, MaxResponseSendWait, false);
CanMessageBuffer::Free(buf);
}
// Send a request message with no reply expected, and don't free the buffer. Used to send emergency stop messages.
void CanInterface::SendMessageNoReplyNoFree(CanMessageBuffer *buf) noexcept
{
- mcan_fd_send_ext_message(buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferBroadcast, MaxResponseSendWait, false);
+ mcan_fd_send_ext_message(&mcan_instance, buf->id.GetWholeId(), reinterpret_cast<uint8_t*>(&(buf->msg)), buf->dataLength, TxBufferBroadcast, MaxResponseSendWait, false);
}
// The CanReceiver task
@@ -768,7 +678,7 @@ extern "C" [[noreturn]] void CanReceiverLoop(void *) noexcept
}
else
{
- GetMessageFromFifo(buf, 0, TaskBase::TimeoutUnlimited);
+ GetMessageFromFifo(&mcan_instance, buf, 0, TaskBase::TimeoutUnlimited);
CommandProcessor::ProcessReceivedMessage(buf);
}
}
@@ -1079,11 +989,11 @@ GCodeResult CanInterface::ChangeAddressAndNormalTiming(GCodeBuffer& gb, const St
{
if (changeTiming)
{
- ChangeLocalCanTiming(timing);
+ ChangeLocalCanTiming(&mcan_instance, timing);
}
else
{
- GetLocalCanTiming(timing);
+ GetLocalCanTiming(&mcan_instance, timing);
reply.printf("CAN bus speed %.1fkbps, tseg1 %.2f, jump width %.2f",
(double)((float)CanTiming::ClockFrequency/(1000 * timing.period)),
(double)((float)timing.tseg1/(float)timing.period),
diff --git a/src/Duet5LC/Pins_Duet5LC.h b/src/Duet5LC/Pins_Duet5LC.h
index d85f017c..dc0dde00 100644
--- a/src/Duet5LC/Pins_Duet5LC.h
+++ b/src/Duet5LC/Pins_Duet5LC.h
@@ -40,7 +40,7 @@ constexpr uint32_t IAP_IMAGE_START = 0x20030000;
#define ENFORCE_MAX_VIN 0
#define HAS_VREF_MONITOR 1
-#define SUPPORT_CAN_EXPANSION 1
+#define SUPPORT_CAN_EXPANSION 0 //TEMP! should be 1
#define SUPPORT_DOTSTAR_LED 1
#define SUPPORT_INKJET 0 // set nonzero to support inkjet control
#define SUPPORT_ROLAND 0 // set nonzero to support Roland mill
diff --git a/src/FilamentMonitors/LaserFilamentMonitor.cpp b/src/FilamentMonitors/LaserFilamentMonitor.cpp
index f0a6d755..ea445a28 100644
--- a/src/FilamentMonitors/LaserFilamentMonitor.cpp
+++ b/src/FilamentMonitors/LaserFilamentMonitor.cpp
@@ -43,12 +43,13 @@ constexpr ObjectModelTableEntry LaserFilamentMonitor::objectModelTable[] =
{ "totalDistance", OBJECT_MODEL_FUNC(self->totalExtrusionCommanded, 1), ObjectModelEntryFlags::none },
// 2. LaserFilamentMonitor.configured members
+ { "calibrationFactor", OBJECT_MODEL_FUNC(self->calibrationFactor, 3), ObjectModelEntryFlags::none },
{ "percentMax", OBJECT_MODEL_FUNC(ConvertToPercent(self->maxMovementAllowed)), ObjectModelEntryFlags::none },
{ "percentMin", OBJECT_MODEL_FUNC(ConvertToPercent(self->minMovementAllowed)), ObjectModelEntryFlags::none },
- { "sampleDistance", OBJECT_MODEL_FUNC(self->minimumExtrusionCheckLength, 1), ObjectModelEntryFlags::none },
+ { "sampleDistance", OBJECT_MODEL_FUNC(self->minimumExtrusionCheckLength, 1), ObjectModelEntryFlags::none },
};
-constexpr uint8_t LaserFilamentMonitor::objectModelTableDescriptor[] = { 3, 5, 4, 3 };
+constexpr uint8_t LaserFilamentMonitor::objectModelTableDescriptor[] = { 3, 5, 4, 4 };
DEFINE_GET_OBJECT_MODEL_TABLE(LaserFilamentMonitor)
@@ -56,6 +57,7 @@ DEFINE_GET_OBJECT_MODEL_TABLE(LaserFilamentMonitor)
LaserFilamentMonitor::LaserFilamentMonitor(unsigned int extruder, unsigned int type) noexcept
: Duet3DFilamentMonitor(extruder, type),
+ calibrationFactor(1.0),
minMovementAllowed(DefaultMinMovementAllowed), maxMovementAllowed(DefaultMaxMovementAllowed),
minimumExtrusionCheckLength(DefaultMinimumExtrusionCheckLength), comparisonEnabled(false), checkNonPrintingMoves(false)
{
@@ -103,6 +105,7 @@ bool LaserFilamentMonitor::Configure(GCodeBuffer& gb, const StringRef& reply, bo
return true;
}
+ gb.TryGetFValue('L', calibrationFactor, seen);
gb.TryGetFValue('E', minimumExtrusionCheckLength, seen);
if (gb.Seen('R'))
@@ -142,11 +145,12 @@ bool LaserFilamentMonitor::Configure(GCodeBuffer& gb, const StringRef& reply, bo
{
reply.printf("Duet3D laser filament monitor v%u%s on pin ", version, (switchOpenMask != 0) ? " with switch" : "");
GetPort().AppendPinName(reply);
- reply.catf(", %s, allow %ld%% to %ld%%, check every %.1fmm, ",
+ reply.catf(", %s, allow %ld%% to %ld%%, check every %.1fmm, calibration factor %.3f, ",
(comparisonEnabled) ? "enabled" : "disabled",
ConvertToPercent(minMovementAllowed),
ConvertToPercent(maxMovementAllowed),
- (double)minimumExtrusionCheckLength);
+ (double)minimumExtrusionCheckLength,
+ (double)calibrationFactor);
if (!dataReceived)
{
@@ -360,18 +364,19 @@ FilamentSensorStatus LaserFilamentMonitor::CheckFilament(float amountCommanded,
}
FilamentSensorStatus ret = FilamentSensorStatus::ok;
+ float extrusionMeasured = amountMeasured * calibrationFactor;
switch (laserMonitorState)
{
case LaserMonitorState::idle:
laserMonitorState = LaserMonitorState::calibrating;
totalExtrusionCommanded = amountCommanded;
- totalMovementMeasured = amountMeasured;
+ totalMovementMeasured = extrusionMeasured;
break;
case LaserMonitorState::calibrating:
totalExtrusionCommanded += amountCommanded;
- totalMovementMeasured += amountMeasured;
+ totalMovementMeasured += extrusionMeasured;
if (totalExtrusionCommanded >= 10.0)
{
backwards = (totalMovementMeasured < 0.0);
@@ -400,10 +405,10 @@ FilamentSensorStatus LaserFilamentMonitor::CheckFilament(float amountCommanded,
totalExtrusionCommanded += amountCommanded;
if (backwards)
{
- amountMeasured = -amountMeasured;
+ extrusionMeasured = -extrusionMeasured;
}
- totalMovementMeasured += amountMeasured;
- const float ratio = amountMeasured/amountCommanded;
+ totalMovementMeasured += extrusionMeasured;
+ const float ratio = extrusionMeasured/amountCommanded;
if (ratio > maxMovementRatio)
{
maxMovementRatio = ratio;
diff --git a/src/FilamentMonitors/LaserFilamentMonitor.h b/src/FilamentMonitors/LaserFilamentMonitor.h
index def1b5d3..cede7f8a 100644
--- a/src/FilamentMonitors/LaserFilamentMonitor.h
+++ b/src/FilamentMonitors/LaserFilamentMonitor.h
@@ -74,6 +74,7 @@ private:
float MeasuredSensitivity() const noexcept;
// Configuration parameters
+ float calibrationFactor;
float minMovementAllowed, maxMovementAllowed;
float minimumExtrusionCheckLength;
bool comparisonEnabled;
diff --git a/src/GCodes/GCodeChannel.h b/src/GCodes/GCodeChannel.h
index b2dbe177..cc36acca 100644
--- a/src/GCodes/GCodeChannel.h
+++ b/src/GCodes/GCodeChannel.h
@@ -11,7 +11,11 @@
#include <RepRapFirmware.h>
#include <General/NamedEnum.h>
-NamedEnum(GCodeChannel, uint8_t, HTTP, Telnet, File, USBchan, Aux, Trigger, Queue, LCD, SBC, Daemon, Aux2, Autopause);
+// The Microchip device library for SAME5x defines USB as the USB peripheral.
+// We can't change the channel name to something else because it breaks compatibility with DSF, so #undef it here
+#undef USB
+
+NamedEnum(GCodeChannel, uint8_t, HTTP, Telnet, File, USB, Aux, Trigger, Queue, LCD, SBC, Daemon, Aux2, Autopause);
constexpr size_t NumGCodeChannels = GCodeChannel::NumValues;
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
index 3c3faa88..668dac35 100644
--- a/src/GCodes/GCodes.cpp
+++ b/src/GCodes/GCodes.cpp
@@ -87,9 +87,9 @@ GCodes::GCodes(Platform& p) noexcept :
#if defined(SERIAL_MAIN_DEVICE)
StreamGCodeInput * const usbInput = new StreamGCodeInput(SERIAL_MAIN_DEVICE);
- usbGCode = new GCodeBuffer(GCodeChannel::USBchan, usbInput, fileInput, UsbMessage, Compatibility::Marlin);
+ usbGCode = new GCodeBuffer(GCodeChannel::USB, usbInput, fileInput, UsbMessage, Compatibility::Marlin);
#elif HAS_LINUX_INTERFACE
- usbGCode = new GCodeBuffer(GCodeChannel::USBchan, nullptr, fileInput, UsbMessage, Compatbility::marlin);
+ usbGCode = new GCodeBuffer(GCodeChannel::USB, nullptr, fileInput, UsbMessage, Compatbility::marlin);
#else
usbGCode = nullptr;
#endif
diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h
index 79de329a..6e0e6c1f 100644
--- a/src/GCodes/GCodes.h
+++ b/src/GCodes/GCodes.h
@@ -144,6 +144,7 @@ public:
bool IsSimulating() const noexcept { return simulationMode != 0; }
bool IsDoingToolChange() const noexcept { return doingToolChange; }
bool IsHeatingUp() const noexcept; // Return true if the SD card print is waiting for a heater to reach temperature
+ bool IsRunningConfigFile() const noexcept { return runningConfigFile; }
uint32_t GetLastDuration() const noexcept { return lastDuration; } // The most recent print time or simulated time
float GetSimulationTime() const noexcept { return simulationTime; }
@@ -471,7 +472,7 @@ private:
GCodeBuffer*& httpGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::HTTP)];
GCodeBuffer*& telnetGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Telnet)];
GCodeBuffer*& fileGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::File)];
- GCodeBuffer*& usbGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::USBchan)];
+ GCodeBuffer*& usbGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::USB)];
GCodeBuffer*& auxGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux)]; // This one is for the PanelDue on the async serial interface
GCodeBuffer*& triggerGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Trigger)]; // Used for executing config.g and trigger macro files
GCodeBuffer*& queuedGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Queue)];
diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp
index 797ba982..eae40d94 100644
--- a/src/GCodes/GCodes2.cpp
+++ b/src/GCodes/GCodes2.cpp
@@ -26,6 +26,8 @@
#include "FilamentMonitors/FilamentMonitor.h"
#include "General/IP4String.h"
#include "Movement/StepperDrivers/DriverMode.h"
+#include <Hardware/SoftwareReset.h>
+#include <Hardware/ExceptionHandlers.h>
#include "Version.h"
#if SUPPORT_IOBITS
@@ -4372,7 +4374,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
reason = (uint16_t)SoftwareResetReason::erase;
}
}
- reprap.SoftwareReset(reason); // doesn't return
+ SoftwareReset(reason); // doesn't return
}
break;
diff --git a/src/Hardware/ExceptionHandlers.cpp b/src/Hardware/ExceptionHandlers.cpp
new file mode 100644
index 00000000..d73492df
--- /dev/null
+++ b/src/Hardware/ExceptionHandlers.cpp
@@ -0,0 +1,283 @@
+/*
+ * CrashHandlers.cpp
+ *
+ * Created on: 24 Aug 2020
+ * Author: David
+ */
+
+#include "ExceptionHandlers.h"
+#include <RepRap.h>
+#include <Platform.h>
+#include <Hardware/NonVolatileMemory.h>
+#include <Cache.h>
+
+// Perform a software reset. 'stk' points to the program counter on the stack if the cause is an exception, otherwise it is nullptr.
+void SoftwareReset(uint16_t reason, const uint32_t *stk) noexcept
+{
+ cpu_irq_disable(); // disable interrupts before we call any flash functions. We don't enable them again.
+ WatchdogReset(); // kick the watchdog
+
+#if SAM4E || SAME70
+ rswdt_restart(RSWDT); // kick the secondary watchdog
+#endif
+
+ Cache::Disable();
+
+#if USE_MPU
+ //TODO set the flash memory to strongly-ordered or device instead
+ ARM_MPU_Disable(); // disable the MPU
+#endif
+
+ if (reason == (uint16_t)SoftwareResetReason::erase)
+ {
+#if SAME5x
+ //TODO invalidate flash so the USB bootloader runs
+#else
+ EraseAndReset();
+#endif
+ }
+ else
+ {
+ if (reason != (uint16_t)SoftwareResetReason::user)
+ {
+ if (SERIAL_MAIN_DEVICE.canWrite() == 0)
+ {
+ reason |= (uint16_t)SoftwareResetReason::inUsbOutput; // if we are resetting because we are stuck in a Spin function, record whether we are trying to send to USB
+ }
+
+#ifdef SERIAL_AUX_DEVICE
+ if (SERIAL_AUX_DEVICE.canWrite() == 0
+# ifdef SERIAL_AUX2_DEVICE
+ || SERIAL_AUX2_DEVICE.canWrite() == 0
+# endif
+ )
+ {
+ reason |= (uint16_t)SoftwareResetReason::inAuxOutput; // if we are resetting because we are stuck in a Spin function, record whether we are trying to send to aux
+ }
+#endif
+ }
+ reason |= (uint8_t)reprap.GetSpinningModule();
+ if (reprap.GetPlatform().WasDeliberateError())
+ {
+ reason |= (uint16_t)SoftwareResetReason::deliberate;
+ }
+
+ // Record the reason for the software reset
+ NonVolatileMemory mem;
+ SoftwareResetData * const srd = mem.AllocateResetDataSlot();
+
+#if defined(__LPC17xx__)
+ srd->Populate(reason, (uint32_t)realTime, stk);
+#else
+ srd->Populate(reason, (uint32_t)reprap.GetPlatform().GetDateTime(), stk);
+#endif
+ mem.EnsureWritten();
+ }
+
+#if defined(__LPC17xx__)
+ LPC_SYSCTL->RSID = 0x3F; // Clear bits in reset reasons stored in RSID
+#elif !SAME5x
+ RSTC->RSTC_MR = RSTC_MR_KEY_PASSWD; // ignore any signal on the NRST pin for now so that the reset reason will show as Software
+#endif
+ Reset();
+ for(;;) {}
+}
+
+// Exception handlers
+// By default the Usage Fault, Bus Fault and Memory Management fault handlers are not enabled,
+// so they escalate to a Hard Fault and we don't need to provide separate exception handlers for them.
+extern "C" [[noreturn]] void hardFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::hardFault, pulFaultStackAddress + 5);
+}
+
+// The fault handler implementation calls a function called hardFaultDispatcher()
+extern "C" [[noreturn]] void HardFault_Handler() noexcept __attribute__((naked));
+void HardFault_Handler() noexcept
+{
+ __asm volatile
+ (
+ " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
+ " ite eq \n" /* load the appropriate stack pointer into R0 */
+ " mrseq r0, msp \n"
+ " mrsne r0, psp \n"
+ " ldr r2, handler_hf_address_const \n"
+ " bx r2 \n"
+ " handler_hf_address_const: .word hardFaultDispatcher \n"
+ );
+}
+
+#if USE_MPU
+
+extern "C" [[noreturn]] void memManageDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::memFault, pulFaultStackAddress + 5);
+}
+
+// The fault handler implementation calls a function called memManageDispatcher()
+extern "C" [[noreturn]] void MemManage_Handler() noexcept __attribute__((naked));
+void MemManage_Handler() noexcept
+{
+ __asm volatile
+ (
+ " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
+ " ite eq \n" /* load the appropriate stack pointer into R0 */
+ " mrseq r0, msp \n"
+ " mrsne r0, psp \n"
+ " ldr r2, handler_mf_address_const \n"
+ " bx r2 \n"
+ " handler_mf_address_const: .word memManageDispatcher \n"
+ );
+}
+
+#endif
+
+extern "C" [[noreturn]] void wdtFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::wdtFault, pulFaultStackAddress + 5);
+}
+
+#ifdef __LPC17xx__
+extern "C" [[noreturn]] void WDT_IRQHandler() noexcept __attribute__((naked));
+void WDT_IRQHandler() noexcept
+{
+ LPC_WDT->MOD &=~((uint32_t)(1<<2)); //SD::clear timout flag before resetting to prevent the Smoothie bootloader going into DFU mode
+#else
+extern "C" [[noreturn]] void WDT_Handler() noexcept __attribute__((naked));
+void WDT_Handler() noexcept
+{
+#endif
+ __asm volatile
+ (
+ " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
+ " ite eq \n" /* load the appropriate stack pointer into R0 */
+ " mrseq r0, msp \n"
+ " mrsne r0, psp \n"
+ " ldr r2, handler_wdt_address_const \n"
+ " bx r2 \n"
+ " handler_wdt_address_const: .word wdtFaultDispatcher \n"
+ );
+}
+
+extern "C" [[noreturn]] void otherFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::otherFault, pulFaultStackAddress + 5);
+}
+
+// 2017-05-25: A user is getting 'otherFault' reports, so now we do a stack dump for those too.
+// The fault handler implementation calls a function called otherFaultDispatcher()
+extern "C" [[noreturn]] void OtherFault_Handler() noexcept __attribute__((naked));
+void OtherFault_Handler() noexcept
+{
+ __asm volatile
+ (
+ " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
+ " ite eq \n" /* load the appropriate stack pointer into R0 */
+ " mrseq r0, msp \n"
+ " mrsne r0, psp \n"
+ " ldr r2, handler_oflt_address_const \n"
+ " bx r2 \n"
+ " handler_oflt_address_const: .word otherFaultDispatcher \n"
+ );
+}
+
+// We could set up the following fault handlers to retrieve the program counter in the same way as for a Hard Fault,
+// however these exceptions are unlikely to occur, so for now we just report the exception type.
+extern "C" [[noreturn]] void NMI_Handler () noexcept { SoftwareReset((uint16_t)SoftwareResetReason::NMI); }
+extern "C" [[noreturn]] void UsageFault_Handler () noexcept { SoftwareReset((uint16_t)SoftwareResetReason::usageFault); }
+
+extern "C" [[noreturn]] void DebugMon_Handler () noexcept __attribute__ ((alias("OtherFault_Handler")));
+
+// FreeRTOS hooks that we need to provide
+extern "C" [[noreturn]] void stackOverflowDispatcher(const uint32_t *pulFaultStackAddress, char* pcTaskName) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::stackOverflow, pulFaultStackAddress);
+}
+
+extern "C" [[noreturn]] void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) noexcept __attribute((naked));
+void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) noexcept
+{
+ // r0 = pxTask, r1 = pxTaskName
+ __asm volatile
+ (
+ " push {r0, r1, lr} \n" /* save parameters and call address on the stack */
+ " mov r0, sp \n"
+ " ldr r2, handler_sovf_address_const \n"
+ " bx r2 \n"
+ " handler_sovf_address_const: .word stackOverflowDispatcher \n"
+ );
+}
+
+extern "C" [[noreturn]] void assertCalledDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::assertCalled, pulFaultStackAddress);
+}
+
+extern "C" [[noreturn]] void vAssertCalled(uint32_t line, const char *file) noexcept __attribute((naked));
+void vAssertCalled(uint32_t line, const char *file) noexcept
+{
+#if false
+ debugPrintf("ASSERTION FAILED IN %s on LINE %d\n", file, line);
+ SERIAL_MAIN_DEVICE.flush();
+#endif
+ __asm volatile
+ (
+ " push {r0, r1, lr} \n" /* save parameters and call address */
+ " mov r0, sp \n"
+ " ldr r2, handler_asrt_address_const \n"
+ " bx r2 \n"
+ " handler_asrt_address_const: .word assertCalledDispatcher \n"
+ );
+}
+
+#ifdef __LPC17xx__
+[[noreturn]] void applicationMallocFailedCalledDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ reprap.SoftwareReset((uint16_t)SoftwareResetReason::assertCalled, pulFaultStackAddress);
+}
+
+[[noreturn]] extern "C" void vApplicationMallocFailedHook() noexcept __attribute((naked));
+void vApplicationMallocFailedHook() noexcept
+{
+ __asm volatile
+ (
+ " push {r0, r1, lr} \n" /* save parameters and call address */
+ " mov r0, sp \n"
+ " ldr r2, handler_amf_address_const \n"
+ " bx r2 \n"
+ " handler_amf_address_const: .word applicationMallocFailedCalledDispatcher \n"
+ );
+}
+#endif
+
+namespace std
+{
+ // We need to define this function in order to use lambda functions with captures
+ [[noreturn]] void __throw_bad_function_call() noexcept { vAssertCalled(__LINE__, __FILE__); }
+}
+
+// The default terminate handler pulls in sprintf and lots of other functions, which makes the binary too large. So we replace it.
+[[noreturn]] void Terminate() noexcept
+{
+ register const uint32_t * stack_ptr asm ("sp");
+ SoftwareReset((uint16_t)SoftwareResetReason::terminateCalled, stack_ptr);
+}
+
+namespace __cxxabiv1
+{
+ std::terminate_handler __terminate_handler = Terminate;
+}
+
+extern "C" [[noreturn]] void __cxa_pure_virtual() noexcept
+{
+ register const uint32_t * stack_ptr asm ("sp");
+ SoftwareReset((uint16_t)SoftwareResetReason::pureVirtual, stack_ptr);
+}
+
+extern "C" [[noreturn]] void __cxa_deleted_virtual() noexcept
+{
+ register const uint32_t * stack_ptr asm ("sp");
+ SoftwareReset((uint16_t)SoftwareResetReason::deletedVirtual, stack_ptr);
+}
+
+// End
diff --git a/src/Hardware/ExceptionHandlers.h b/src/Hardware/ExceptionHandlers.h
new file mode 100644
index 00000000..a5082add
--- /dev/null
+++ b/src/Hardware/ExceptionHandlers.h
@@ -0,0 +1,15 @@
+/*
+ * CrashHandlers.h
+ *
+ * Created on: 24 Aug 2020
+ * Author: David
+ */
+
+#ifndef SRC_HARDWARE_EXCEPTIONHANDLERS_H_
+#define SRC_HARDWARE_EXCEPTIONHANDLERS_H_
+
+#include <cstdint>
+
+[[noreturn]] void SoftwareReset(uint16_t reason, const uint32_t *stk = nullptr) noexcept;
+
+#endif /* SRC_HARDWARE_EXCEPTIONHANDLERS_H_ */
diff --git a/src/Hardware/NonVolatileMemory.cpp b/src/Hardware/NonVolatileMemory.cpp
new file mode 100644
index 00000000..1e08d6d2
--- /dev/null
+++ b/src/Hardware/NonVolatileMemory.cpp
@@ -0,0 +1,171 @@
+/*
+ * NonVolatileMemory.cpp
+ *
+ * Created on: 24 Aug 2020
+ * Author: David
+ */
+
+#include "NonVolatileMemory.h"
+
+#if SAM4E || SAM4S || SAME70
+# include <Cache.h>
+# include <flash_efc.h>
+#endif
+
+NonVolatileMemory::NonVolatileMemory() noexcept : state(NvmState::notRead)
+{
+}
+
+void NonVolatileMemory::EnsureRead() noexcept
+{
+ if (state == NvmState::notRead)
+ {
+#if SAME5x
+ memcpy(&buffer, reinterpret_cast<const void *>(SEEPROM_ADDR), sizeof(buffer));
+#elif defined(__LPC17xx__)
+ qq; //TODO
+#elif SAM4E || SAM4S || SAME70
+ // Work around bug in ASF flash library: flash_read_user_signature calls a RAMFUNC without disabling interrupts first.
+ // This caused a crash (watchdog timeout) sometimes if we run M122 while a print is in progress
+ const irqflags_t flags = cpu_irq_save();
+ Cache::Disable();
+ flash_read_user_signature(reinterpret_cast<uint32_t*>(&buffer), sizeof(buffer)/sizeof(uint32_t));
+ Cache::Enable();
+ cpu_irq_restore(flags);
+#else
+# error Unsupported processor
+#endif
+ if (buffer.magic != NVM::MagicValue)
+ {
+ memset(&buffer, 0xFF, sizeof(buffer));
+ buffer.magic = NVM::MagicValue;
+ state = NvmState::eraseAndWriteNeeded;
+ }
+ else
+ {
+ state = NvmState::clean;
+ }
+ }
+}
+
+void NonVolatileMemory::EnsureWritten() noexcept
+{
+#if SAME5x
+ if (state >= NvmState::writeNeeded)
+ {
+ while (NVMCTRL->SEESTAT.bit.BUSY) { }
+ memcpy(reinterpret_cast<uint8_t*>(SEEPROM_ADDR), &buffer, sizeof(buffer));
+ state = NvmState::clean;
+ }
+#else
+ if (state == NvmState::eraseAndWriteNeeded)
+ {
+ // Erase the page
+# if SAM4E || SAM4S || SAME70
+ flash_erase_user_signature();
+# elif defined(__LPC17xx__)
+ LPC_EraseSoftwareResetDataSlots(); // erase the last flash sector
+# endif
+ state = NvmState::writeNeeded;
+ }
+
+ if (state == NvmState::writeNeeded)
+ {
+# if SAM4E || SAM4S || SAME70
+ flash_write_user_signature(&buffer, sizeof(buffer)/sizeof(uint32_t));
+# elif defined(__LPC17xx__)
+ LPC_WriteSoftwareResetData(slot, buffer, sizeof(buffer));
+# else
+# error Unsupported processor
+# endif
+ state = NvmState::clean;
+ }
+#endif
+}
+
+SoftwareResetData* NonVolatileMemory::GetLastWrittenResetData(unsigned int &slot) noexcept
+{
+ EnsureRead();
+ for (unsigned int i = NumberOfResetDataSlots; i != 0; )
+ {
+ --i;
+ if (buffer.resetData[i].IsValid())
+ {
+ slot = i;
+ return &buffer.resetData[i];
+ }
+ }
+ return nullptr;
+}
+
+SoftwareResetData* NonVolatileMemory::AllocateResetDataSlot() noexcept
+{
+ EnsureRead();
+ for (unsigned int i = 0; i < NumberOfResetDataSlots; ++i)
+ {
+ if (buffer.resetData[i].IsVacant())
+ {
+ state = NvmState::writeNeeded; // assume the caller will write to the allocated slot
+ return &buffer.resetData[i];
+ }
+ }
+
+ // All slots are full, so clear them out and start again
+ for (unsigned int i = 0; i < NumberOfResetDataSlots; ++i)
+ {
+ buffer.resetData[i].Clear();
+ }
+ state = NvmState::eraseAndWriteNeeded;
+ return &buffer.resetData[0];
+}
+
+int8_t NonVolatileMemory::GetThermistorLowCalibration(unsigned int inputNumber) noexcept
+{
+ return GetThermistorCalibration(inputNumber, buffer.thermistorLowCalibration);
+}
+
+int8_t NonVolatileMemory::GetThermistorHighCalibration(unsigned int inputNumber) noexcept
+{
+ return GetThermistorCalibration(inputNumber, buffer.thermistorHighCalibration);
+}
+
+void NonVolatileMemory::SetThermistorLowCalibration(unsigned int inputNumber, int8_t val) noexcept
+{
+ SetThermistorCalibration(inputNumber, val, buffer.thermistorLowCalibration);
+}
+
+void NonVolatileMemory::SetThermistorHighCalibration(unsigned int inputNumber, int8_t val) noexcept
+{
+ SetThermistorCalibration(inputNumber, val, buffer.thermistorHighCalibration);
+}
+
+int8_t NonVolatileMemory::GetThermistorCalibration(unsigned int inputNumber, uint8_t *calibArray) noexcept
+{
+ EnsureRead();
+ return (inputNumber >= MaxCalibratedThermistors || calibArray[inputNumber] == 0xFF) ? 0 : calibArray[inputNumber] - 0x7F;
+}
+
+void NonVolatileMemory::SetThermistorCalibration(unsigned int inputNumber, int8_t val, uint8_t *calibArray) noexcept
+{
+ if (inputNumber < MaxCalibratedThermistors)
+ {
+ EnsureRead();
+ const uint8_t oldVal = calibArray[inputNumber];
+ const uint8_t newVal = val + 0x7F;
+ if (oldVal != newVal)
+ {
+ // If we are only changing 1 bits to 0 then we don't need to erase
+ calibArray[inputNumber] = newVal;
+ if ((newVal & ~oldVal) != 0)
+ {
+ state = NvmState::eraseAndWriteNeeded;
+ }
+ else if (state == NvmState::clean)
+ {
+ state = NvmState::writeNeeded;
+ }
+ }
+ }
+}
+
+// End
diff --git a/src/Hardware/NonVolatileMemory.h b/src/Hardware/NonVolatileMemory.h
new file mode 100644
index 00000000..d5175bbb
--- /dev/null
+++ b/src/Hardware/NonVolatileMemory.h
@@ -0,0 +1,59 @@
+/*
+ * NonVolatileMemory.h
+ *
+ * Class to manage an area of flash memory that we use to store data that persists over a reset
+ * Created on: 24 Aug 2020
+ * Author: David
+ */
+
+#ifndef SRC_HARDWARE_NONVOLATILEMEMORY_H_
+#define SRC_HARDWARE_NONVOLATILEMEMORY_H_
+
+#include <Hardware/SoftwareReset.h>
+
+// This class manages nonvolatile settings that are specific to the board, and the software reset data that is stored by the crash handler.
+// On most Duets there is a 512-byte User Page that we use for this.
+// The SAMC21 and SAME5x processors already store various data in the user page, however both those processor families support EEPROM emulation so we use 512 bytes of that instead.
+
+class NonVolatileMemory
+{
+public:
+ NonVolatileMemory() noexcept;
+
+ void EnsureWritten() noexcept;
+ SoftwareResetData *GetLastWrittenResetData(unsigned int &slot) noexcept;
+ SoftwareResetData *AllocateResetDataSlot() noexcept;
+ int8_t GetThermistorLowCalibration(unsigned int inputNumber) noexcept;
+ int8_t GetThermistorHighCalibration(unsigned int inputNumber) noexcept;
+ void SetThermistorLowCalibration(unsigned int inputNumber, int8_t val) noexcept;
+ void SetThermistorHighCalibration(unsigned int inputNumber, int8_t val) noexcept;
+
+ static constexpr unsigned int NumberOfResetDataSlots = 3;
+ static constexpr unsigned int MaxCalibratedThermistors = 8;
+
+private:
+ void EnsureRead() noexcept;
+ int8_t GetThermistorCalibration(unsigned int inputNumber, uint8_t *calibArray) noexcept;
+ void SetThermistorCalibration(unsigned int inputNumber, int8_t val, uint8_t *calibArray) noexcept;
+
+ struct NVM
+ {
+ uint16_t magic;
+ uint8_t thermistorLowCalibration[MaxCalibratedThermistors]; // currently used only by SAME70-based boards
+ uint8_t thermistorHighCalibration[MaxCalibratedThermistors]; // currently used only by SAME70-based boards
+ uint8_t spare[38];
+ // 56 bytes up to here
+ SoftwareResetData resetData[NumberOfResetDataSlots]; // 3 slots of 152 bytes each
+
+ static constexpr uint32_t MagicValue = 0x41E5;
+ };
+
+ static_assert(sizeof(NVM) == 512);
+
+ enum class NvmState : uint8_t { notRead, clean, writeNeeded, eraseAndWriteNeeded };
+
+ alignas(4) NVM buffer;
+ NvmState state;
+};
+
+#endif /* SRC_HARDWARE_NONVOLATILEMEMORY_H_ */
diff --git a/src/Hardware/SAME70/CanDriver.cpp b/src/Hardware/SAME70/CanDriver.cpp
index d9e75711..d91f1847 100644
--- a/src/Hardware/SAME70/CanDriver.cpp
+++ b/src/Hardware/SAME70/CanDriver.cpp
@@ -318,20 +318,17 @@ void mcan_init(mcan_module *const module_inst, Mcan *hw, struct mcan_config *con
/* Associate the software module instance with the hardware module */
module_inst->hw = hw;
+ module_inst->taskWaitingOnFifo[0] = module_inst->taskWaitingOnFifo[1] = nullptr;
pmc_disable_pck(PMC_PCK_5);
-#if 1 // dc42 use UPLL not PLLA as recommended in the documentation, so the MCAN clock is independent of the CPU clock frequency
+ // Use UPLL so the MCAN clock is independent of the CPU clock frequency
pmc_switch_pck_to_upllck(PMC_PCK_5, PMC_PCK_PRES(9)); // run PCLK5 at 48MHz
-#else
- pmc_switch_pck_to_pllack(PMC_PCK_5, PMC_PCK_PRES(9));
-#endif
pmc_enable_pck(PMC_PCK_5);
/* Enable peripheral clock */
_mcan_enable_peripheral_clock(module_inst);
-
/* Configuration Change Enable. */
hw->MCAN_CCCR |= MCAN_CCCR_CCE;
@@ -739,6 +736,90 @@ bool WaitForTxBufferFree(mcan_module *const module_inst, uint32_t whichTxBuffer,
return true;
}
+// Get a message from a FIFO with timeout. Return true if successful, false if we timed out
+bool GetMessageFromFifo(mcan_module *const module_inst, CanMessageBuffer *buf, unsigned int fifoNumber, uint32_t timeout) noexcept
+{
+ volatile uint32_t* const fifoRegisters = &(module_inst->hw->MCAN_RXF0S) + (4 * fifoNumber); // pointer to FIFO status register followed by FIFO acknowledge register
+ module_inst->taskWaitingOnFifo[fifoNumber] = TaskBase::GetCallerTaskHandle();
+ while (true)
+ {
+ const uint32_t status = fifoRegisters[0]; // get FIFO status
+ if ((status & MCAN_RXF0S_F0FL_Msk) != 0) // if there are any messages
+ {
+ const uint32_t getIndex = (status & MCAN_RXF0S_F0GI_Msk) >> MCAN_RXF0S_F0GI_Pos;
+ mcan_rx_element elem;
+ if (fifoNumber == 1)
+ {
+ mcan_get_rx_fifo_1_element(module_inst, &elem, getIndex); // copy the data (TODO use our own driver, avoid double copying)
+ }
+ else
+ {
+ mcan_get_rx_fifo_0_element(module_inst, &elem, getIndex); // copy the data (TODO use our own driver, avoid double copying)
+ }
+ fifoRegisters[1] = getIndex; // acknowledge it, release the FIFO entry
+
+ if (elem.R0.bit.XTD == 1 && elem.R0.bit.RTR != 1) // if extended address and not a remote frame
+ {
+ // Copy the message and accompanying data to our buffer
+ buf->id.SetReceivedId(elem.R0.bit.ID);
+ buf->dataLength = dlc2len[elem.R1.bit.DLC];
+ memcpy(buf->msg.raw, elem.data, buf->dataLength);
+ module_inst->taskWaitingOnFifo[fifoNumber] = nullptr;
+ return true;
+ }
+ }
+ else if (!TaskBase::Take(timeout))
+ {
+ module_inst->taskWaitingOnFifo[fifoNumber] = nullptr;
+ return false;
+ }
+ }
+}
+
+void GetLocalCanTiming(mcan_module *const module_inst, CanTiming& timing) noexcept
+{
+ const uint32_t nbtp = module_inst->hw->MCAN_NBTP;
+ const uint32_t tseg1 = (nbtp & MCAN_NBTP_NTSEG1_Msk) >> MCAN_NBTP_NTSEG1_Pos;
+ const uint32_t tseg2 = (nbtp & MCAN_NBTP_NTSEG2_Msk) >> MCAN_NBTP_NTSEG2_Pos;
+ const uint32_t jw = (nbtp & MCAN_NBTP_NSJW_Msk) >> MCAN_NBTP_NSJW_Pos;
+ const uint32_t brp = (nbtp & MCAN_NBTP_NBRP_Msk) >> MCAN_NBTP_NBRP_Pos;
+ timing.period = (tseg1 + tseg2 + 3) * (brp + 1);
+ timing.tseg1 = (tseg1 + 1) * (brp + 1);
+ timing.jumpWidth = (jw + 1) * (brp + 1);
+}
+
+void ChangeLocalCanTiming(mcan_module *const module_inst, const CanTiming& timing) noexcept
+{
+ // Sort out the bit timing
+ uint32_t period = timing.period;
+ uint32_t tseg1 = timing.tseg1;
+ uint32_t jumpWidth = timing.jumpWidth;
+ uint32_t prescaler = 1; // 48MHz main clock
+ uint32_t tseg2;
+
+ for (;;)
+ {
+ tseg2 = period - tseg1 - 1;
+ if (tseg1 <= 32 && tseg2 <= 16 && jumpWidth <= 16)
+ {
+ break;
+ }
+ prescaler <<= 1;
+ period >>= 1;
+ tseg1 >>= 1;
+ jumpWidth >>= 1;
+ }
+
+ //TODO stop transmissions in an orderly fashion, or postpone initialising CAN until we have the timing data
+ module_inst->hw->MCAN_CCCR |= MCAN_CCCR_CCE | MCAN_CCCR_INIT;
+ module_inst->hw->MCAN_NBTP = ((tseg1 - 1) << MCAN_NBTP_NTSEG1_Pos)
+ | ((tseg2 - 1) << MCAN_NBTP_NTSEG2_Pos)
+ | ((jumpWidth - 1) << MCAN_NBTP_NSJW_Pos)
+ | ((prescaler - 1) << MCAN_NBTP_NBRP_Pos);
+ module_inst->hw->MCAN_CCCR &= ~MCAN_CCCR_CCE;
+}
+
+
#endif // SUPPORT_CAN_EXPANSION
// End
diff --git a/src/Hardware/SAME70/CanDriver.h b/src/Hardware/SAME70/CanDriver.h
index ae2f5290..bd505233 100644
--- a/src/Hardware/SAME70/CanDriver.h
+++ b/src/Hardware/SAME70/CanDriver.h
@@ -42,7 +42,9 @@
#if SUPPORT_CAN_EXPANSION
-#include <compiler.h>
+#include <CanSettings.h>
+#include <CanMessageBuffer.h>
+#include <RTOSIface/RTOSIface.h>
#include <status_codes.h>
/** The value should be 8/12/16/20/24/32/48/64. */
@@ -529,8 +531,10 @@ enum mcan_nonmatching_frames_action {
* \note The fields of this structure should not be altered by the user
* application; they are reserved for module-internal use only.
*/
-struct mcan_module {
+struct mcan_module
+{
Mcan *hw;
+ TaskHandle taskWaitingOnFifo[2];
};
/**
@@ -692,6 +696,12 @@ status_code mcan_fd_send_ext_message_no_wait(mcan_module *const module_inst, uin
// Return true if we cancelled the pending transmission.
bool WaitForTxBufferFree(mcan_module *const module_inst, uint32_t whichTxBuffer, uint32_t maxWait) noexcept;
+// Get a message from a FIFO with timeout. Return true if successful, false if we timed out
+bool GetMessageFromFifo(mcan_module *const module_inst, CanMessageBuffer *buf, unsigned int fifoNumber, uint32_t timeout) noexcept;
+
+void GetLocalCanTiming(mcan_module *const module_inst, CanTiming& timing) noexcept;
+void ChangeLocalCanTiming(mcan_module *const module_inst, const CanTiming& timing) noexcept;
+
/**
* \brief Can read timestamp count value.
*
diff --git a/src/SoftwareReset.cpp b/src/Hardware/SoftwareReset.cpp
index 1dea1844..9ed16c34 100644
--- a/src/SoftwareReset.cpp
+++ b/src/Hardware/SoftwareReset.cpp
@@ -34,7 +34,7 @@ const char *const SoftwareResetData::ReasonText[] =
uint8_t SoftwareResetData::extraDebugInfo; // extra info for debugging
// Return true if this struct can be written without erasing it first
-bool SoftwareResetData::isVacant() const noexcept
+bool SoftwareResetData::IsVacant() const noexcept
{
const uint32_t *p = reinterpret_cast<const uint32_t*>(this);
for (size_t i = 0; i < sizeof(*this)/sizeof(uint32_t); ++i)
@@ -48,6 +48,11 @@ bool SoftwareResetData::isVacant() const noexcept
return true;
}
+void SoftwareResetData::Clear() noexcept
+{
+ memset(this, 0xFF, sizeof(SoftwareResetData));
+}
+
// Populate this reset data from the parameters passed and the CPU state
void SoftwareResetData::Populate(uint16_t reason, uint32_t time, const uint32_t *stk) noexcept
{
diff --git a/src/SoftwareReset.h b/src/Hardware/SoftwareReset.h
index f18f7f3d..e95cc2f8 100644
--- a/src/SoftwareReset.h
+++ b/src/Hardware/SoftwareReset.h
@@ -68,38 +68,18 @@ struct SoftwareResetData
uint32_t sp; // stack pointer
uint32_t when; // value of the RTC when the software reset occurred
uint32_t taskName; // first 4 bytes of the task name
- uint32_t stack[23]; // stack when the exception occurred, with the program counter at the bottom
+ uint32_t stack[29]; // stack when the exception occurred, with the link register and program counter at the bottom
- bool isVacant() const noexcept; // return true if this struct can be written without erasing it first
+ bool IsVacant() const noexcept; // return true if this struct can be written without erasing it first
+ bool IsValid() const noexcept { return magic == magicValue; }
+ void Clear() noexcept;
void Populate(uint16_t reason, uint32_t time, const uint32_t *stk) noexcept;
- static const uint16_t versionValue = 8; // increment this whenever this struct changes
+ static const uint16_t versionValue = 9; // increment this whenever this struct changes
static const uint16_t magicValue = 0x7D00 | versionValue; // value we use to recognise that all the flash data has been written
-#ifdef __LPC17xx__
- // Software Reset Data is stored in Flash. Since the LPC1768/9 doesn't have the page erase IAP command,
- // so we have to use the whole sector (last sector of flash is used)
- // The last sector size is 32k. IAP requires us to write at least 256 bytes and the destination address needs to be on a 256 byte boundary.
- // Therefore can have 32k/256=128 slots and we will fill the entire sector before erasing it
- static const size_t numberOfSlots = 128; // number of storage slots used to implement wear levelling
-#else
- static const size_t numberOfSlots = 4; // number of storage slots used to implement wear levelling - must fit in 512 bytes
-#endif
-
-#if SAM3XA
- static const uint32_t nvAddress = 0; // must be 4-byte aligned
-#endif
-
static const char *const ReasonText[];
static uint8_t extraDebugInfo; // extra info for debugging can be stored here
};
-#if SAM4E || SAM4S || SAME70
-static_assert(SoftwareResetData::numberOfSlots * sizeof(SoftwareResetData) <= 512, "Can't fit software reset data in user signature area");
-#elif SAME5x
-// TODO
-#else
-static_assert(SoftwareResetData::numberOfSlots * sizeof(SoftwareResetData) <= FLASH_DATA_LENGTH, "NVData too large");
-#endif
-
#endif /* SRC_SOFTWARERESET_H_ */
diff --git a/src/Heating/Sensors/Thermistor.cpp b/src/Heating/Sensors/Thermistor.cpp
index 25010d8c..56f5aed6 100644
--- a/src/Heating/Sensors/Thermistor.cpp
+++ b/src/Heating/Sensors/Thermistor.cpp
@@ -11,6 +11,11 @@
#include "RepRap.h"
#include "GCodes/GCodeBuffer/GCodeBuffer.h"
+#ifdef DUET3
+# include <GCodes/GCodes.h>
+# include <Hardware/NonVolatileMemory.h>
+#endif
+
// The Steinhart-Hart equation for thermistor resistance is:
// 1/T = A + B ln(R) + C [ln(R)]^3
//
@@ -54,6 +59,22 @@ GCodeResult Thermistor::Configure(GCodeBuffer& gb, const StringRef& reply, bool&
return GCodeResult::error;
}
+ if (changed)
+ {
+ // We changed the port, so set up the ADC filter
+ adcFilterChannel = reprap.GetPlatform().GetAveragingFilterIndex(port);
+ if (adcFilterChannel >= 0)
+ {
+ reprap.GetPlatform().GetAdcFilter(adcFilterChannel).Init((1u << AdcBits) - 1);
+#ifdef DUET3
+ // Default the H and L parameters to the values from nonvolatile memory
+ NonVolatileMemory mem;
+ adcLowOffset = mem.GetThermistorLowCalibration(adcFilterChannel);
+ adcHighOffset = mem.GetThermistorHighCalibration(adcFilterChannel);
+#endif
+ }
+ }
+
gb.TryGetFValue('R', seriesR, changed);
if (!isPT1000)
{
@@ -92,16 +113,25 @@ GCodeResult Thermistor::Configure(GCodeBuffer& gb, const StringRef& reply, bool&
reply.copy("Measured L correction for port \"");
port.AppendPinName(reply);
reply.catf("\" is %d", adcLowOffset);
- // TODO store the value in NVM
+
+ // Store the value in NVM
+ if (!reprap.GetGCodes().IsRunningConfigFile())
+ {
+ NonVolatileMemory mem;
+ mem.SetThermistorLowCalibration(adcFilterChannel, adcLowOffset);
+ mem.EnsureWritten();
+ }
}
else
{
reply.copy("Computed correction is not valid. Check that you have placed a jumper across the thermistor input.");
+ return GCodeResult::error;
}
}
else
{
reply.copy("Temperature reading is not valid");
+ return GCodeResult::error;
}
}
else
@@ -130,16 +160,25 @@ GCodeResult Thermistor::Configure(GCodeBuffer& gb, const StringRef& reply, bool&
reply.copy("Measured H correction for port \"");
port.AppendPinName(reply);
reply.catf("\" is %d", adcHighOffset);
- // TODO store the value in NVM
+
+ // Store the value in NVM
+ if (!reprap.GetGCodes().IsRunningConfigFile())
+ {
+ NonVolatileMemory mem;
+ mem.SetThermistorHighCalibration(adcFilterChannel, adcHighOffset);
+ mem.EnsureWritten();
+ }
}
else
{
reply.copy("Computed correction is not valid. Check that you have disconnected the thermistor.");
+ return GCodeResult::error;
}
}
else
{
reply.copy("Temperature reading is not valid");
+ return GCodeResult::error;
}
}
else
@@ -153,15 +192,7 @@ GCodeResult Thermistor::Configure(GCodeBuffer& gb, const StringRef& reply, bool&
TryConfigureSensorName(gb, changed);
- if (changed)
- {
- adcFilterChannel = reprap.GetPlatform().GetAveragingFilterIndex(port);
- if (adcFilterChannel >= 0)
- {
- reprap.GetPlatform().GetAdcFilter(adcFilterChannel).Init((1u << AdcBits) - 1);
- }
- }
- else
+ if (!changed)
{
CopyBasicDetails(reply);
if (isPT1000)
@@ -178,10 +209,14 @@ GCodeResult Thermistor::Configure(GCodeBuffer& gb, const StringRef& reply, bool&
#endif
if (reprap.Debug(moduleHeat) && adcFilterChannel >= 0)
{
+#if HAS_VREF_MONITOR
reply.catf(", Vref %" PRIu32 " Vssa %" PRIu32 " Th %" PRIu32,
reprap.GetPlatform().GetAdcFilter(VrefFilterIndex).GetSum(),
reprap.GetPlatform().GetAdcFilter(VssaFilterIndex).GetSum(),
reprap.GetPlatform().GetAdcFilter(adcFilterChannel).GetSum());
+#else
+ reply.catf(", Th %" PRIu32, reprap.GetPlatform().GetAdcFilter(adcFilterChannel).GetSum());
+#endif
}
}
diff --git a/src/Linux/LinuxInterface.cpp b/src/Linux/LinuxInterface.cpp
index 77600b63..2761dbe4 100644
--- a/src/Linux/LinuxInterface.cpp
+++ b/src/Linux/LinuxInterface.cpp
@@ -18,6 +18,8 @@
#include "Tools/Filament.h"
#include "RepRap.h"
#include "RepRapFirmware.h"
+#include <Hardware/SoftwareReset.h>
+#include <Hardware/ExceptionHandlers.h>
#include <Cache.h>
Mutex LinuxInterface::gcodeReplyMutex;
@@ -76,7 +78,7 @@ void LinuxInterface::Spin() noexcept
// Reset the controller
case LinuxRequest::Reset:
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::user);
+ SoftwareReset((uint16_t)SoftwareResetReason::user);
return;
// Perform a G/M/T-code
diff --git a/src/Platform.cpp b/src/Platform.cpp
index cf56dcf0..7388fa20 100644
--- a/src/Platform.cpp
+++ b/src/Platform.cpp
@@ -39,6 +39,7 @@
#include "Hardware/SharedSpi/SharedSpiDevice.h"
#include "Math/Isqrt.h"
#include "Hardware/I2C.h"
+#include <Hardware/NonVolatileMemory.h>
#if SAME70
# include <DmacManager.h>
@@ -1805,7 +1806,20 @@ void Platform::Diagnostics(MessageType mtype) noexcept
resetString.cat('\n');
Message(mtype, resetString.c_str());
}
-#elif !defined(__LPC17xx__)
+#elif defined(__LPC17xx__)
+ // Reset Reason
+ MessageF(mtype, "Last reset %02d:%02d:%02d ago, cause: ",
+ (unsigned int)(now/3600), (unsigned int)((now % 3600)/60), (unsigned int)(now % 60));
+
+ if (LPC_SYSCTL->RSID & RSID_POR) { MessageF(mtype, "[power up]"); }
+ if (LPC_SYSCTL->RSID & RSID_EXTR) { MessageF(mtype, "[reset button]"); }
+ if (LPC_SYSCTL->RSID & RSID_WDTR) { MessageF(mtype, "[watchdog]"); }
+ if (LPC_SYSCTL->RSID & RSID_BODR) { MessageF(mtype, "[brownout]"); }
+ if (LPC_SYSCTL->RSID & RSID_SYSRESET) { MessageF(mtype, "[software]"); }
+ if (LPC_SYSCTL->RSID & RSID_LOCKUP) { MessageF(mtype, "[lockup]"); }
+
+ MessageF(mtype, "\n");
+#else
const char* resetReasons[8] = { "power up", "backup", "watchdog", "software",
# ifdef DUET_NG
// On the SAM4E a watchdog reset may be reported as a user reset because of the capacitor on the NRST pin.
@@ -1818,77 +1832,24 @@ void Platform::Diagnostics(MessageType mtype) noexcept
MessageF(mtype, "Last reset %02d:%02d:%02d ago, cause: %s\n",
(unsigned int)(now/3600), (unsigned int)((now % 3600)/60), (unsigned int)(now % 60),
resetReasons[(REG_RSTC_SR & RSTC_SR_RSTTYP_Msk) >> RSTC_SR_RSTTYP_Pos]);
-#endif //end ifndef __LPC17xx__
+#endif
// Show the reset code stored at the last software reset
{
-#if defined(__LPC17xx__)
- // Reset Reason
- MessageF(mtype, "Last reset %02d:%02d:%02d ago, cause: ",
- (unsigned int)(now/3600), (unsigned int)((now % 3600)/60), (unsigned int)(now % 60));
-
- if (LPC_SYSCTL->RSID & RSID_POR) { MessageF(mtype, "[power up]"); }
- if (LPC_SYSCTL->RSID & RSID_EXTR) { MessageF(mtype, "[reset button]"); }
- if (LPC_SYSCTL->RSID & RSID_WDTR) { MessageF(mtype, "[watchdog]"); }
- if (LPC_SYSCTL->RSID & RSID_BODR) { MessageF(mtype, "[brownout]"); }
- if (LPC_SYSCTL->RSID & RSID_SYSRESET) { MessageF(mtype, "[software]"); }
- if (LPC_SYSCTL->RSID & RSID_LOCKUP) { MessageF(mtype, "[lockup]"); }
-
- MessageF(mtype, "\n");
- SoftwareResetData srdBuf[1];
- int slot = -1;
-
- for (int s = SoftwareResetData::numberOfSlots - 1; s >= 0; s--)
- {
- SoftwareResetData *sptr = reinterpret_cast<SoftwareResetData *>(LPC_GetSoftwareResetDataSlotPtr(s));
- if (sptr->magic != 0xFFFF)
- {
- //slot = s;
- MessageF(mtype, "LPC Flash Slot[%d]: \n", s);
- slot = 0; // we only have 1 slot in the array, set this to zero to be compatible with existing code below
- //copy the data into srdBuff
- LPC_ReadSoftwareResetDataSlot(s, &srdBuf[0], sizeof(srdBuf[0]));
- break;
- }
- }
-#else
- SoftwareResetData srdBuf[SoftwareResetData::numberOfSlots];
- memset(srdBuf, 0, sizeof(srdBuf));
- int slot = -1;
-
-#if SAME5x
- memcpy(srdBuf, reinterpret_cast<const void *>(SEEPROM_ADDR), sizeof(srdBuf));
-#elif SAM4E || SAM4S || SAME70
- // Work around bug in ASF flash library: flash_read_user_signature calls a RAMFUNC without disabling interrupts first.
- // This caused a crash (watchdog timeout) sometimes if we run M122 while a print is in progress
- const irqflags_t flags = cpu_irq_save();
- Cache::Disable();
- const uint32_t rc = flash_read_user_signature(reinterpret_cast<uint32_t*>(srdBuf), sizeof(srdBuf)/sizeof(uint32_t));
- Cache::Enable();
- cpu_irq_restore(flags);
-
- if (rc == FLASH_RC_OK)
-# else
- DueFlashStorage::read(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf));
-# endif
+ NonVolatileMemory mem;
+ unsigned int slot;
+ const SoftwareResetData * const srd = mem.GetLastWrittenResetData(slot);
+ if (srd == nullptr)
{
- // Find the last slot written
- slot = SoftwareResetData::numberOfSlots;
- do
- {
- --slot;
- }
- while (slot >= 0 && srdBuf[slot].magic == 0xFFFF);
+ Message(mtype, "Last software reset details not available\n");
}
-#endif
-
- if (slot >= 0 && srdBuf[slot].magic == SoftwareResetData::magicValue)
+ else
{
- const char* const reasonText = SoftwareResetData::ReasonText[(srdBuf[slot].resetReason >> 5) & 0x0F];
+ const char* const reasonText = SoftwareResetData::ReasonText[(srd->resetReason >> 5) & 0x0F];
String<StringLength100> scratchString;
- if (srdBuf[slot].when != 0)
+ if (srd->when != 0)
{
- const time_t when = (time_t)srdBuf[slot].when;
+ const time_t when = (time_t)srd->when;
tm timeInfo;
gmtime_r(&when, &timeInfo);
scratchString.printf("at %04u-%02u-%02u %02u:%02u",
@@ -1901,31 +1862,27 @@ void Platform::Diagnostics(MessageType mtype) noexcept
MessageF(mtype, "Last software reset %s, reason: %s%s, spinning module %s, available RAM %" PRIu32 " bytes (slot %d)\n",
scratchString.c_str(),
- (srdBuf[slot].resetReason & (uint32_t)SoftwareResetReason::deliberate) ? "deliberate " : "",
+ (srd->resetReason & (uint32_t)SoftwareResetReason::deliberate) ? "deliberate " : "",
reasonText,
- GetModuleName(srdBuf[slot].resetReason & 0x1F), srdBuf[slot].neverUsedRam, slot);
+ GetModuleName(srd->resetReason & 0x1F), srd->neverUsedRam, slot);
// Our format buffer is only 256 characters long, so the next 2 lines must be written separately
// The task name may include nulls at the end, so print it as a string
- const uint32_t taskName[2] = { srdBuf[slot].taskName, 0 };
+ const uint32_t taskName[2] = { srd->taskName, 0 };
MessageF(mtype,
"Software reset code 0x%04x HFSR 0x%08" PRIx32 " CFSR 0x%08" PRIx32 " ICSR 0x%08" PRIx32 " BFAR 0x%08" PRIx32 " SP 0x%08" PRIx32 " Task %s\n",
- srdBuf[slot].resetReason, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].icsr, srdBuf[slot].bfar, srdBuf[slot].sp, (const char *)&taskName
+ srd->resetReason, srd->hfsr, srd->cfsr, srd->icsr, srd->bfar, srd->sp, (const char *)&taskName
);
- if (srdBuf[slot].sp != 0xFFFFFFFF)
+ if (srd->sp != 0xFFFFFFFF)
{
// We saved a stack dump, so print it
scratchString.Clear();
- for (uint32_t stval : srdBuf[slot].stack)
+ for (uint32_t stval : srd->stack)
{
scratchString.catf(" %08" PRIx32, stval);
}
MessageF(mtype, "Stack:%s\n", scratchString.c_str());
}
}
- else
- {
- Message(mtype, "Last software reset details not available\n");
- }
}
// Show the current error codes
@@ -2228,7 +2185,7 @@ GCodeResult Platform::DiagnosticTest(GCodeBuffer& gb, const StringRef& reply, Ou
if (gb.TryGetUIValue('V', val, dummy))
{
*reinterpret_cast<uint32_t*>(address) = val;
- delayMicroseconds(10); // allow some time for a fault to be raised
+ __DSB(); // allow the write to complete in case it raises a fault
}
else
{
@@ -2433,7 +2390,8 @@ void Platform::DriverCoolingFansOnOff(DriverChannelsBitmap driverChannelsMonitor
#endif
-// Get the index of the averaging filter for an analog port
+// Get the index of the averaging filter for an analog port.
+// Note, the Thermistor code assumes that this is also the thermistor input number
int Platform::GetAveragingFilterIndex(const IoPort& port) const noexcept
{
for (size_t i = 0; i < NumAdcFilters; ++i)
diff --git a/src/Platform.h b/src/Platform.h
index 0e4ed4c2..a62f023c 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -62,6 +62,7 @@ constexpr bool FORWARDS = true;
constexpr bool BACKWARDS = !FORWARDS;
// Define the number of ADC filters and the indices of the extra ones
+// Note, the thermistor code assumes that the first N filters are used by the TEMP0 to TEMP(N-1) thermistor inputs, where N = NumThermistorInputs
#if HAS_VREF_MONITOR
constexpr size_t VrefFilterIndex = NumThermistorInputs;
constexpr size_t VssaFilterIndex = NumThermistorInputs + 1;
diff --git a/src/RepRap.cpp b/src/RepRap.cpp
index 11f5c25b..e496b753 100644
--- a/src/RepRap.cpp
+++ b/src/RepRap.cpp
@@ -19,6 +19,8 @@
#include "Tasks.h"
#include <Cache.h>
#include "Fans/FansManager.h"
+#include <Hardware/SoftwareReset.h>
+#include <Hardware/ExceptionHandlers.h>
#include "Version.h"
#ifdef DUET_NG
@@ -858,134 +860,6 @@ void RepRap::EmergencyStop() noexcept
platform->StopLogging();
}
-// Perform a software reset. 'stk' points to the program counter on the stack if the cause is an exception, otherwise it is nullptr.
-void RepRap::SoftwareReset(uint16_t reason, const uint32_t *stk) noexcept
-{
- cpu_irq_disable(); // disable interrupts before we call any flash functions. We don't enable them again.
- WatchdogReset(); // kick the watchdog
-
-#if SAM4E || SAME70
- rswdt_restart(RSWDT); // kick the secondary watchdog
-#endif
-
- Cache::Disable();
-
-#if USE_MPU
- //TODO set the flash memory to strongly-ordered or device instead
- ARM_MPU_Disable(); // disable the MPU
-#endif
-
- if (reason == (uint16_t)SoftwareResetReason::erase)
- {
-#if SAME5x
- //TODO invalidate flash so the USB bootloader runs
-#else
- EraseAndReset();
-#endif
- }
- else
- {
- if (reason != (uint16_t)SoftwareResetReason::user)
- {
- if (SERIAL_MAIN_DEVICE.canWrite() == 0)
- {
- reason |= (uint16_t)SoftwareResetReason::inUsbOutput; // if we are resetting because we are stuck in a Spin function, record whether we are trying to send to USB
- }
-
-#ifdef SERIAL_AUX_DEVICE
- if (SERIAL_AUX_DEVICE.canWrite() == 0
-# ifdef SERIAL_AUX2_DEVICE
- || SERIAL_AUX2_DEVICE.canWrite() == 0
-# endif
- )
- {
- reason |= (uint16_t)SoftwareResetReason::inAuxOutput; // if we are resetting because we are stuck in a Spin function, record whether we are trying to send to aux
- }
-#endif
- }
- reason |= (uint8_t)reprap.GetSpinningModule();
- if (platform->WasDeliberateError())
- {
- reason |= (uint16_t)SoftwareResetReason::deliberate;
- }
-
- // Record the reason for the software reset
- // First find a free slot (wear levelling)
- size_t slot = SoftwareResetData::numberOfSlots;
-#if defined(__LPC17xx__)
- SoftwareResetData srdBuf[1];
-#else
- SoftwareResetData srdBuf[SoftwareResetData::numberOfSlots];
-#endif
-
-#if SAME5x
- memcpy(srdBuf, reinterpret_cast<const void*>(SEEPROM_ADDR), sizeof(srdBuf));
-#elif SAM4E || SAM4S || SAME70
- if (flash_read_user_signature(reinterpret_cast<uint32_t*>(srdBuf), sizeof(srdBuf)/sizeof(uint32_t)) == FLASH_RC_OK)
-#elif SAM3XA
- DueFlashStorage::read(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf));
-#elif defined(__LPC17xx__)
- // nothing here
-#else
-# error
-#endif
- {
-#if defined(__LPC17xx__)
- while (slot != 0 && LPC_IsSoftwareResetDataSlotVacant(slot - 1))
-#else
- while (slot != 0 && srdBuf[slot - 1].isVacant())
-#endif
- {
- --slot;
- }
- }
-
- if (slot == SoftwareResetData::numberOfSlots)
- {
- // No free slots, so erase the area
-#if SAME5x
- memset(reinterpret_cast<void*>(SEEPROM_ADDR), 0xFF, sizeof(srdBuf));
-#elif SAM4E || SAM4S || SAME70
- flash_erase_user_signature();
-#elif defined(__LPC17xx__)
- LPC_EraseSoftwareResetDataSlots(); // erase the last flash sector
-#endif
- memset(srdBuf, 0xFF, sizeof(srdBuf));
- slot = 0;
- }
-
-#if defined(__LPC17xx__)
- srdBuf[0].Populate(reason, (uint32_t)realTime, stk);
-#else
- srdBuf[slot].Populate(reason, (uint32_t)platform->GetDateTime(), stk);
-#endif
-
- // Save diagnostics data to Flash
-#if SAME5x
- while (NVMCTRL->SEESTAT.bit.BUSY) { }
- memcpy(reinterpret_cast<uint8_t*>(SEEPROM_ADDR) + (slot * sizeof(SoftwareResetData)), &srdBuf[slot], sizeof(SoftwareResetData));
-#elif SAM4E || SAM4S || SAME70
- flash_write_user_signature(srdBuf, sizeof(srdBuf)/sizeof(uint32_t));
-#elif defined(__LPC17xx__)
- LPC_WriteSoftwareResetData(slot, srdBuf, sizeof(srdBuf));
-#else
- DueFlashStorage::write(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf));
-#endif
- }
-
-#if defined(__LPC17xx__)
- LPC_SYSCTL->RSID = 0x3F; // Clear bits in reset reasons stored in RSID
-#elif !SAME5x
-# ifndef RSTC_MR_KEY_PASSWD
-// Definition of RSTC_MR_KEY_PASSWD is missing in the SAM3X ASF files
-# define RSTC_MR_KEY_PASSWD (0xA5u << 24)
-# endif
- RSTC->RSTC_MR = RSTC_MR_KEY_PASSWD; // ignore any signal on the NRST pin for now so that the reset reason will show as Software
-#endif
- Reset();
- for(;;) {}
-}
-
void RepRap::SetDebug(Module m, bool enable) noexcept
{
if (m < numModules)
diff --git a/src/RepRap.h b/src/RepRap.h
index e6e1d4d4..a34bc91f 100644
--- a/src/RepRap.h
+++ b/src/RepRap.h
@@ -31,7 +31,6 @@ Licence: GPL
# include <CAN/ExpansionManager.h>
#endif
-#include "SoftwareReset.h"
#include <functional>
enum class ResponseSource
@@ -174,7 +173,6 @@ public:
[[noreturn]] void StartIap() noexcept;
void ReportInternalError(const char *file, const char *func, int line) const noexcept; // report an internal error
- [[noreturn]] void SoftwareReset(uint16_t reason, const uint32_t *stk = nullptr) noexcept;
static uint32_t DoDivide(uint32_t a, uint32_t b) noexcept; // helper function for diagnostic tests
static void GenerateBusFault() noexcept; // helper function for diagnostic tests
diff --git a/src/Tasks.cpp b/src/Tasks.cpp
index 662957ec..43cbb65d 100644
--- a/src/Tasks.cpp
+++ b/src/Tasks.cpp
@@ -10,6 +10,7 @@
#include "Platform.h"
#include <Cache.h>
#include <TaskPriorities.h>
+#include <Hardware/SoftwareReset.h>
#if SAME5x
# include <DmacManager.h>
@@ -302,213 +303,11 @@ const Mutex *Tasks::GetSysDirMutex() noexcept
return &sysDirMutex;
}
-// Exception handlers
-extern "C"
+// This intercepts the 1ms system tick
+extern "C" void vApplicationTickHook() noexcept
{
- // This intercepts the 1ms system tick
- void vApplicationTickHook() noexcept
- {
- CoreSysTick();
- reprap.Tick();
- }
-
- // Exception handlers
- // By default the Usage Fault, Bus Fault and Memory Management fault handlers are not enabled,
- // so they escalate to a Hard Fault and we don't need to provide separate exception handlers for them.
- [[noreturn]] void hardFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
- {
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::hardFault, pulFaultStackAddress + 5);
- }
-
- // The fault handler implementation calls a function called hardFaultDispatcher()
- [[noreturn]] void HardFault_Handler() noexcept __attribute__((naked));
- void HardFault_Handler() noexcept
- {
- __asm volatile
- (
- " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
- " ite eq \n" /* load the appropriate stack pointer into R0 */
- " mrseq r0, msp \n"
- " mrsne r0, psp \n"
- " ldr r2, handler_hf_address_const \n"
- " bx r2 \n"
- " handler_hf_address_const: .word hardFaultDispatcher \n"
- );
- }
-
-#if USE_MPU
-
- [[noreturn]] void memManageDispatcher(const uint32_t *pulFaultStackAddress) noexcept
- {
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::memFault, pulFaultStackAddress + 5);
- }
-
- // The fault handler implementation calls a function called memManageDispatcher()
- [[noreturn]] void MemManage_Handler() noexcept __attribute__((naked));
- void MemManage_Handler() noexcept
- {
- __asm volatile
- (
- " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
- " ite eq \n" /* load the appropriate stack pointer into R0 */
- " mrseq r0, msp \n"
- " mrsne r0, psp \n"
- " ldr r2, handler_mf_address_const \n"
- " bx r2 \n"
- " handler_mf_address_const: .word memManageDispatcher \n"
- );
- }
-
-#endif
-
- [[noreturn]] void wdtFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
- {
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::wdtFault, pulFaultStackAddress + 5);
- }
-
-#ifdef __LPC17xx__
- [[noreturn]] void WDT_IRQHandler() noexcept __attribute__((naked));
- void WDT_IRQHandler() noexcept
- {
- LPC_WDT->MOD &=~((uint32_t)(1<<2)); //SD::clear timout flag before resetting to prevent the Smoothie bootloader going into DFU mode
-#else
- [[noreturn]] void WDT_Handler() noexcept __attribute__((naked));
- void WDT_Handler() noexcept
- {
-#endif
- __asm volatile
- (
- " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
- " ite eq \n" /* load the appropriate stack pointer into R0 */
- " mrseq r0, msp \n"
- " mrsne r0, psp \n"
- " ldr r2, handler_wdt_address_const \n"
- " bx r2 \n"
- " handler_wdt_address_const: .word wdtFaultDispatcher \n"
- );
- }
-
- [[noreturn]] void otherFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
- {
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::otherFault, pulFaultStackAddress + 5);
- }
-
- // 2017-05-25: A user is getting 'otherFault' reports, so now we do a stack dump for those too.
- // The fault handler implementation calls a function called otherFaultDispatcher()
- [[noreturn]] void OtherFault_Handler() noexcept __attribute__((naked));
- void OtherFault_Handler() noexcept
- {
- __asm volatile
- (
- " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
- " ite eq \n" /* load the appropriate stack pointer into R0 */
- " mrseq r0, msp \n"
- " mrsne r0, psp \n"
- " ldr r2, handler_oflt_address_const \n"
- " bx r2 \n"
- " handler_oflt_address_const: .word otherFaultDispatcher \n"
- );
- }
-
- // We could set up the following fault handlers to retrieve the program counter in the same way as for a Hard Fault,
- // however these exceptions are unlikely to occur, so for now we just report the exception type.
- [[noreturn]] void NMI_Handler () noexcept { reprap.SoftwareReset((uint16_t)SoftwareResetReason::NMI); }
- [[noreturn]] void UsageFault_Handler () noexcept { reprap.SoftwareReset((uint16_t)SoftwareResetReason::usageFault); }
-
- [[noreturn]] void DebugMon_Handler () noexcept __attribute__ ((alias("OtherFault_Handler")));
-
- // FreeRTOS hooks that we need to provide
- [[noreturn]] void stackOverflowDispatcher(const uint32_t *pulFaultStackAddress, char* pcTaskName) noexcept
- {
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::stackOverflow, pulFaultStackAddress);
- }
-
- [[noreturn]] void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) noexcept __attribute((naked));
- void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) noexcept
- {
- // r0 = pxTask, r1 = pxTaskName
- __asm volatile
- (
- " push {r0, r1, lr} \n" /* save parameters and call address on the stack */
- " mov r0, sp \n"
- " ldr r2, handler_sovf_address_const \n"
- " bx r2 \n"
- " handler_sovf_address_const: .word stackOverflowDispatcher \n"
- );
- }
-
- [[noreturn]] void assertCalledDispatcher(const uint32_t *pulFaultStackAddress) noexcept
- {
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::assertCalled, pulFaultStackAddress);
- }
-
- [[noreturn]] void vAssertCalled(uint32_t line, const char *file) noexcept __attribute((naked));
- void vAssertCalled(uint32_t line, const char *file) noexcept
- {
-#if false
- debugPrintf("ASSERTION FAILED IN %s on LINE %d\n", file, line);
- SERIAL_MAIN_DEVICE.flush();
-#endif
- __asm volatile
- (
- " push {r0, r1, lr} \n" /* save parameters and call address */
- " mov r0, sp \n"
- " ldr r2, handler_asrt_address_const \n"
- " bx r2 \n"
- " handler_asrt_address_const: .word assertCalledDispatcher \n"
- );
- }
-
-#ifdef __LPC17xx__
- [[noreturn]] void applicationMallocFailedCalledDispatcher(const uint32_t *pulFaultStackAddress) noexcept
- {
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::assertCalled, pulFaultStackAddress);
- }
-
- [[noreturn]] void vApplicationMallocFailedHook() noexcept __attribute((naked));
- void vApplicationMallocFailedHook() noexcept
- {
- __asm volatile
- (
- " push {r0, r1, lr} \n" /* save parameters and call address */
- " mov r0, sp \n"
- " ldr r2, handler_amf_address_const \n"
- " bx r2 \n"
- " handler_amf_address_const: .word applicationMallocFailedCalledDispatcher \n"
- );
- }
-#endif
-
-} // end extern "C"
-
-namespace std
-{
- // We need to define this function in order to use lambda functions with captures
- [[noreturn]] void __throw_bad_function_call() noexcept { vAssertCalled(__LINE__, __FILE__); }
-}
-
-// The default terminate handler pulls in sprintf and lots of other functions, which makes the binary too large. So we replace it.
-[[noreturn]] void Terminate() noexcept
-{
- register const uint32_t * stack_ptr asm ("sp");
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::terminateCalled, stack_ptr);
-}
-
-namespace __cxxabiv1
-{
- std::terminate_handler __terminate_handler = Terminate;
-}
-
-extern "C" [[noreturn]] void __cxa_pure_virtual() noexcept
-{
- register const uint32_t * stack_ptr asm ("sp");
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::pureVirtual, stack_ptr);
-}
-
-extern "C" [[noreturn]] void __cxa_deleted_virtual() noexcept
-{
- register const uint32_t * stack_ptr asm ("sp");
- reprap.SoftwareReset((uint16_t)SoftwareResetReason::deletedVirtual, stack_ptr);
+ CoreSysTick();
+ reprap.Tick();
}
// We don't need the time zone functionality. Declaring these saves 8Kb.
diff --git a/src/Tools/Spindle.cpp b/src/Tools/Spindle.cpp
index c6617c33..a3b0e0c9 100644
--- a/src/Tools/Spindle.cpp
+++ b/src/Tools/Spindle.cpp
@@ -21,10 +21,10 @@ constexpr ObjectModelTableEntry Spindle::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. Spindle members
- { "active", OBJECT_MODEL_FUNC(self->configuredRpm), ObjectModelEntryFlags::none },
- { "current", OBJECT_MODEL_FUNC(self->currentRpm), ObjectModelEntryFlags::live },
+ { "active", OBJECT_MODEL_FUNC(self->configuredRpm, 1), ObjectModelEntryFlags::none },
+ { "current", OBJECT_MODEL_FUNC(self->currentRpm, 1), ObjectModelEntryFlags::live },
{ "frequency", OBJECT_MODEL_FUNC((int32_t)self->frequency), ObjectModelEntryFlags::verbose },
- { "max", OBJECT_MODEL_FUNC(self->maxRpm), ObjectModelEntryFlags::verbose },
+ { "max", OBJECT_MODEL_FUNC(self->maxRpm, 1), ObjectModelEntryFlags::verbose },
{ "tool", OBJECT_MODEL_FUNC((int32_t)self->toolNumber), ObjectModelEntryFlags::verbose },
};
diff --git a/src/Version.h b/src/Version.h
index 96252e54..b28f37d4 100644
--- a/src/Version.h
+++ b/src/Version.h
@@ -19,7 +19,7 @@
#endif
#ifndef DATE
-# define DATE "2020-08-23b2"
+# define DATE "2020-08-28b1"
#endif
#define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman, printm3d"