Compare commits
39 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
48d4d920be | |
|
|
b2dd038814 | |
|
|
3aed994b9c | |
|
|
e089be4ff8 | |
|
|
9e3b300b2e | |
|
|
9c23658445 | |
|
|
3d32c300a3 | |
|
|
f4388b8d20 | |
|
|
5d8fa524fb | |
|
|
42cb40eeb9 | |
|
|
06f41bf1e6 | |
|
|
b6bdd55def | |
|
|
c1aa190d05 | |
|
|
88a241c741 | |
|
|
7a250c24db | |
|
|
39b24f8b43 | |
|
|
22a8f1df57 | |
|
|
ceda1cce08 | |
|
|
5e50516fe7 | |
|
|
454619bfd4 | |
|
|
f240bb24d1 | |
|
|
b00ac883c4 | |
|
|
9e44c06f1e | |
|
|
e9889ec066 | |
|
|
4e4e2df739 | |
|
|
b8ed9abc7f | |
|
|
e28ab28824 | |
|
|
255dc8726a | |
|
|
b23d6827a7 | |
|
|
dc68b70cff | |
|
|
0eeef4cafd | |
|
|
4ab07d627a | |
|
|
0a205e41ff | |
|
|
3c8663d900 | |
|
|
195aad9cf0 | |
|
|
e7abc6a7d8 | |
|
|
32d5f3b4be | |
|
|
ca1d09ad15 | |
|
|
89d3c6fc47 |
|
|
@ -11,7 +11,8 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
|
||||
# setup base required dependencies
|
||||
|
|
@ -36,7 +37,7 @@ jobs:
|
|||
submodules: true
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: actions/cache@v4.0.0
|
||||
uses: actions/cache@v4
|
||||
id: cache-pkgs
|
||||
with:
|
||||
path: ".dependencies"
|
||||
|
|
@ -58,7 +59,7 @@ jobs:
|
|||
|
||||
- name: Upload artifacts
|
||||
if: ${{ !github.event.pull_request }}
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Firmware
|
||||
path: build/*.hex
|
||||
|
|
@ -71,73 +72,3 @@ jobs:
|
|||
draft: true
|
||||
files: |
|
||||
build/autopublish/*.hex
|
||||
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
|
||||
# setup base required dependencies
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-11 g++11 lcov cmake ninja-build python3-pyelftools python3-regex python3-polib
|
||||
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- name: Checkout ${{ github.event.pull_request.head.ref }}
|
||||
uses: actions/checkout@v4
|
||||
if: ${{ github.event.pull_request }}
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
submodules: true
|
||||
|
||||
- name: Checkout ${{ github.event.ref }}
|
||||
uses: actions/checkout@v4
|
||||
if: ${{ !github.event.pull_request }}
|
||||
with:
|
||||
ref: ${{ github.event.ref }}
|
||||
submodules: true
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: actions/cache@v4.0.0
|
||||
id: cache-pkgs
|
||||
with:
|
||||
path: ".dependencies"
|
||||
key: "build-deps-1_0_0-linux"
|
||||
|
||||
- name: Setup build dependencies
|
||||
run: |
|
||||
./utils/bootstrap.py
|
||||
|
||||
- name: Cache permissions
|
||||
run: sudo chmod -R 744 .dependencies
|
||||
|
||||
- name: Build
|
||||
id: tests_run
|
||||
continue-on-error: true
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G Ninja
|
||||
ninja test_coverage_report
|
||||
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
name: Coverage
|
||||
path: build/Coverage
|
||||
|
||||
- name: Add PR Comment
|
||||
if: ${{ github.event.pull_request }}
|
||||
uses: mshick/add-pr-comment@v2.8.2
|
||||
with:
|
||||
message-path: build/Summary.txt
|
||||
message-id: "coverage"
|
||||
|
||||
- name: Report failure
|
||||
if: steps.tests_run.outcome == 'failure'
|
||||
run: echo ${{ steps.tests_run.outcome }} && test -n ""
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: actions/cache@v4.0.0
|
||||
uses: actions/cache@v4
|
||||
id: cache-pkgs
|
||||
with:
|
||||
path: ".dependencies"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ on:
|
|||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
name: tests
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
branches: [ main, MMU_* ]
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
|
||||
# setup base required dependencies
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-11 g++11 lcov cmake ninja-build python3-pyelftools python3-regex python3-polib
|
||||
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- name: Checkout ${{ github.event.pull_request.head.ref }}
|
||||
uses: actions/checkout@v4
|
||||
if: ${{ github.event.pull_request }}
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
submodules: true
|
||||
|
||||
- name: Checkout ${{ github.event.ref }}
|
||||
uses: actions/checkout@v4
|
||||
if: ${{ !github.event.pull_request }}
|
||||
with:
|
||||
ref: ${{ github.event.ref }}
|
||||
submodules: true
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: actions/cache@v4
|
||||
id: cache-pkgs
|
||||
with:
|
||||
path: ".dependencies"
|
||||
key: "build-deps-1_0_0-linux"
|
||||
|
||||
- name: Setup build dependencies
|
||||
run: |
|
||||
./utils/bootstrap.py
|
||||
|
||||
- name: Cache permissions
|
||||
run: sudo chmod -R 744 .dependencies
|
||||
|
||||
- name: Build
|
||||
id: tests_run
|
||||
continue-on-error: true
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G Ninja
|
||||
ninja test_coverage_report
|
||||
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Coverage
|
||||
path: build/Coverage
|
||||
|
||||
- name: Add PR Comment
|
||||
if: ${{ github.event.pull_request }}
|
||||
uses: mshick/add-pr-comment@v2.8.2
|
||||
with:
|
||||
message-path: build/Summary.txt
|
||||
message-id: "coverage"
|
||||
|
||||
- name: Report failure
|
||||
if: steps.tests_run.outcome == 'failure'
|
||||
run: echo ${{ steps.tests_run.outcome }} && test -n ""
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
"name": "avr-gcc",
|
||||
"toolchainFile": "${workspaceFolder}/cmake/AvrGcc.cmake",
|
||||
"cmakeSettings": {
|
||||
"CMAKE_MAKE_PROGRAM": "${workspaceFolder}/.dependencies/ninja-1.10.2/ninja",
|
||||
"CMAKE_MAKE_PROGRAM": "${workspaceFolder}/.dependencies/ninja-1.12.1/ninja",
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Run `./utils/bootstrap.py`
|
|||
`bootstrap.py` will now download all the "missing" dependencies into the `.dependencies` folder:
|
||||
- clang-format-9.0.0-noext
|
||||
- cmake-3.22.5
|
||||
- ninja-1.10.2
|
||||
- ninja-1.12.1
|
||||
- avr-gcc-7.3.0
|
||||
|
||||
### How to build the preliminary project so far:
|
||||
|
|
|
|||
|
|
@ -42,14 +42,14 @@
|
|||
#include "lufa/LUFA/Drivers/USB/USB.h"
|
||||
|
||||
/* Macros: */
|
||||
/** Endpoint address of the CDC device-to-host notification IN endpoint. */
|
||||
#define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | 2)
|
||||
|
||||
/** Endpoint address of the CDC device-to-host data IN endpoint. */
|
||||
#define CDC_TX_EPADDR (ENDPOINT_DIR_IN | 3)
|
||||
#define CDC_TX_EPADDR (ENDPOINT_DIR_IN | 1)
|
||||
|
||||
/** Endpoint address of the CDC host-to-device data OUT endpoint. */
|
||||
#define CDC_RX_EPADDR (ENDPOINT_DIR_OUT | 4)
|
||||
#define CDC_RX_EPADDR (ENDPOINT_DIR_OUT | 2)
|
||||
|
||||
/** Endpoint address of the CDC device-to-host notification IN endpoint. */
|
||||
#define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | 3)
|
||||
|
||||
/** Size in bytes of the CDC device-to-host notification IN endpoint. */
|
||||
#define CDC_NOTIFICATION_EPSIZE 8
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
#define USB_DEVICE_ONLY
|
||||
#define DEVICE_STATE_AS_GPIOR 0
|
||||
// #define ORDERED_EP_CONFIG
|
||||
#define ORDERED_EP_CONFIG
|
||||
#define FIXED_CONTROL_ENDPOINT_SIZE 8
|
||||
#define FIXED_NUM_CONFIGURATIONS 1
|
||||
#define INTERRUPT_CONTROL_ENDPOINT
|
||||
#define USE_FLASH_DESCRIPTORS
|
||||
#define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)
|
||||
#define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_MANUAL_PLL)
|
||||
#define NO_INTERNAL_SERIAL
|
||||
#define NO_DEVICE_SELF_POWER
|
||||
#define NO_DEVICE_REMOTE_WAKEUP
|
||||
// #define NO_SOF_EVENTS
|
||||
#define NO_SOF_EVENTS
|
||||
#define F_USB F_CPU
|
||||
#define DEVICE_VID 0x2C99
|
||||
#define DEVICE_PID 0x0004
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ void Application::ProcessRequestMsg(const mp::RequestMsg &rq) {
|
|||
case mp::RequestMsgCodes::Eject:
|
||||
case mp::RequestMsgCodes::Home:
|
||||
case mp::RequestMsgCodes::Load:
|
||||
case mp::RequestMsgCodes::Mode:
|
||||
case mp::RequestMsgCodes::Tool:
|
||||
case mp::RequestMsgCodes::Unload:
|
||||
PlanCommand(rq);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ struct AxisConfig {
|
|||
uint8_t iHold; ///< holding current
|
||||
bool stealth; ///< Default to Stealth mode
|
||||
long double stepsPerUnit; ///< steps per unit
|
||||
long double stepsPerUnitReciprocal; ///< reciprocal of step per unit (used to avoid divisions)
|
||||
int8_t sg_thrs; /// @todo 7bit two's complement for the sg_thrs
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ static constexpr U_mm pulleyHelperMove = 10.0_mm; ///< Helper move for Load/Unlo
|
|||
static constexpr U_mm cutLength = 8.0_mm;
|
||||
static constexpr U_mm fsensorToNozzle = 30.0_mm; ///< ~20mm from MK4's filament sensor through extruder gears into nozzle
|
||||
static constexpr U_mm fsensorToNozzleAvoidGrind = 5.0_mm;
|
||||
static constexpr U_mm fsensorToNozzleAvoidGrindUnload = 20.0_mm;
|
||||
/// Check the state of FSensor after this amount of filament got (hopefully) pulled out while unloading.
|
||||
static constexpr U_mm fsensorUnloadCheckDistance = 40.0_mm;
|
||||
|
||||
|
|
@ -115,6 +116,7 @@ static constexpr AxisConfig pulley = {
|
|||
.iHold = 0, /// 17mA in SpreadCycle, freewheel in StealthChop
|
||||
.stealth = false,
|
||||
.stepsPerUnit = (200 * 8 / 19.147274),
|
||||
.stepsPerUnitReciprocal = 1 / ((200 * 8 / 19.147274)),
|
||||
.sg_thrs = 8,
|
||||
};
|
||||
|
||||
|
|
@ -140,6 +142,7 @@ static constexpr AxisConfig selector = {
|
|||
.iHold = 0, /// 17mA in SpreadCycle, freewheel in StealthChop
|
||||
.stealth = false,
|
||||
.stepsPerUnit = (200 * 8 / 8.),
|
||||
.stepsPerUnitReciprocal = 1 / ((200 * 8 / 8.)),
|
||||
.sg_thrs = 3,
|
||||
};
|
||||
|
||||
|
|
@ -190,6 +193,7 @@ static constexpr AxisConfig idler = {
|
|||
.iHold = 5, /// 99mA - parked current
|
||||
.stealth = false,
|
||||
.stepsPerUnit = (200 * 16 / 360.),
|
||||
.stepsPerUnitReciprocal = 1 / ((200 * 16 / 360.)),
|
||||
.sg_thrs = 7,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -52,31 +52,31 @@ struct GPIO_pin {
|
|||
// No constructor here in order to allow brace-initialization in old
|
||||
// gcc versions/standards
|
||||
GPIO_TypeDef *const port;
|
||||
const uint8_t pin;
|
||||
const uint8_t pin_mask;
|
||||
};
|
||||
|
||||
__attribute__((always_inline)) inline void WritePin(const GPIO_pin portPin, Level level) {
|
||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||
if (level == Level::high)
|
||||
portPin.port->PORTx |= (1 << portPin.pin);
|
||||
portPin.port->PORTx |= portPin.pin_mask;
|
||||
else
|
||||
portPin.port->PORTx &= ~(1 << portPin.pin);
|
||||
portPin.port->PORTx &= ~portPin.pin_mask;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline Level ReadPin(const GPIO_pin portPin) {
|
||||
#ifdef __AVR__
|
||||
return (Level)((portPin.port->PINx & (1 << portPin.pin)) != 0);
|
||||
return (Level)((portPin.port->PINx & portPin.pin_mask) != 0);
|
||||
#else
|
||||
// Return the value modified by WritePin
|
||||
return (Level)((portPin.port->PORTx & (1 << portPin.pin)) != 0);
|
||||
return (Level)((portPin.port->PORTx & portPin.pin_mask) != 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void TogglePin(const GPIO_pin portPin) {
|
||||
#ifdef __AVR__
|
||||
// Optimized path for AVR, resulting in a pin toggle
|
||||
portPin.port->PINx = (1 << portPin.pin);
|
||||
portPin.port->PINx = portPin.pin_mask;
|
||||
#else
|
||||
WritePin(portPin, (Level)(ReadPin(portPin) != Level::high));
|
||||
#endif
|
||||
|
|
@ -85,9 +85,9 @@ __attribute__((always_inline)) inline void TogglePin(const GPIO_pin portPin) {
|
|||
__attribute__((always_inline)) inline void Init(const GPIO_pin portPin, GPIO_InitTypeDef GPIO_Init) {
|
||||
if (GPIO_Init.mode == Mode::output) {
|
||||
WritePin(portPin, GPIO_Init.level);
|
||||
portPin.port->DDRx |= (1 << portPin.pin);
|
||||
portPin.port->DDRx |= portPin.pin_mask;
|
||||
} else {
|
||||
portPin.port->DDRx &= ~(1 << portPin.pin);
|
||||
portPin.port->DDRx &= ~portPin.pin_mask;
|
||||
WritePin(portPin, (Level)GPIO_Init.pull);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ void CommandBase::ErrDisengagingIdler() {
|
|||
void CommandBase::GoToErrDisengagingIdler(ErrorCode deferredEC) {
|
||||
state = ProgressCode::ERRDisengagingIdler;
|
||||
deferredErrorCode = deferredEC;
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::blink0);
|
||||
ml::leds.ActiveSlotError();
|
||||
mi::idler.Disengage();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ bool CutFilament::StepInner() {
|
|||
// move selector aside - prepare the blade into active position
|
||||
state = ProgressCode::PreparingBlade;
|
||||
mg::globals.SetFilamentLoaded(cutSlot, mg::FilamentLoadState::AtPulley);
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
|
||||
ml::leds.ActiveSlotProcessing();
|
||||
MoveSelector(cutSlot + 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ bool CutFilament::StepInner() {
|
|||
case ProgressCode::PreparingBlade:
|
||||
if (ms::selector.Slot() == cutSlot + 1) {
|
||||
state = ProgressCode::PushingFilament;
|
||||
mpu::pulley.PlanMove(mg::globals.CutLength() + config::cuttingEdgeRetract, config::pulleySlowFeedrate);
|
||||
mpu::pulley.PlanMove(mg::globals.CutLength() + config::cuttingEdgeRetract, mg::globals.PulleySlowFeedrate_mm_s());
|
||||
}
|
||||
break;
|
||||
case ProgressCode::PushingFilament:
|
||||
|
|
@ -123,7 +123,7 @@ bool CutFilament::StepInner() {
|
|||
// revert move speed
|
||||
mg::globals.SetSelectorFeedrate_mm_s(savedSelectorFeedRate_mm_s);
|
||||
ms::selector.InvalidateHoming();
|
||||
mpu::pulley.PlanMove(-config::cuttingEdgeRetract, config::pulleySlowFeedrate);
|
||||
mpu::pulley.PlanMove(-config::cuttingEdgeRetract, mg::globals.PulleySlowFeedrate_mm_s());
|
||||
}
|
||||
break;
|
||||
case ProgressCode::Homing:
|
||||
|
|
@ -134,7 +134,7 @@ bool CutFilament::StepInner() {
|
|||
case ProgressCode::ReturningSelector:
|
||||
if (ms::selector.State() == ms::selector.Ready) {
|
||||
FinishedOK();
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::on, ml::off);
|
||||
ml::leds.ActiveSlotDonePrimed();
|
||||
}
|
||||
break;
|
||||
case ProgressCode::OK:
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ bool EjectFilament::StepInner() {
|
|||
if (mi::idler.Engaged()) {
|
||||
state = ProgressCode::EjectingFilament;
|
||||
mpu::pulley.InitAxis();
|
||||
mpu::pulley.PlanMove(config::ejectFromCuttingEdge, config::pulleySlowFeedrate);
|
||||
mpu::pulley.PlanMove(config::ejectFromCuttingEdge, mg::globals.PulleySlowFeedrate_mm_s());
|
||||
}
|
||||
break;
|
||||
case ProgressCode::EjectingFilament:
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ void FeedToBondtech::Reset(uint8_t maxRetries) {
|
|||
dbg_logic_P(PSTR("\nFeed to Bondtech\n\n"));
|
||||
state = EngagingIdler;
|
||||
this->maxRetries = maxRetries;
|
||||
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::blink0);
|
||||
ml::leds.ActiveSlotProcessing();
|
||||
mi::idler.Engage(mg::globals.ActiveSlot());
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ bool FeedToBondtech::Step() {
|
|||
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
|
||||
mi::idler.PartiallyDisengage(mg::globals.ActiveSlot());
|
||||
// while disengaging the idler, keep on moving with the pulley to avoid grinding while the printer is trying to grab the filament itself
|
||||
mpu::pulley.PlanMove(config::fsensorToNozzleAvoidGrind, config::pulleySlowFeedrate);
|
||||
mpu::pulley.PlanMove(config::fsensorToNozzleAvoidGrind, mg::globals.PulleySlowFeedrate_mm_s());
|
||||
state = PartiallyDisengagingIdler;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -95,7 +95,7 @@ bool FeedToBondtech::Step() {
|
|||
dbg_logic_P(PSTR("Feed to Bondtech --> Idler disengaged"));
|
||||
dbg_logic_fP(PSTR("Pulley end steps %u"), mpu::pulley.CurrentPosition_mm());
|
||||
state = OK;
|
||||
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on);
|
||||
ml::leds.ActiveSlotDonePrimed();
|
||||
}
|
||||
return false;
|
||||
case OK:
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace logic {
|
|||
/// To prevent constant EEPROM updates only significant changes are recorded.
|
||||
struct FeedToBondtech {
|
||||
/// internal states of the state machine
|
||||
enum {
|
||||
enum : uint8_t {
|
||||
EngagingIdler,
|
||||
PushingFilamentFast,
|
||||
PushingFilamentToFSensor,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ bool FeedToFinda::Reset(bool feedPhaseLimited, bool haltAtEnd) {
|
|||
state = EngagingIdler;
|
||||
this->feedPhaseLimited = feedPhaseLimited;
|
||||
this->haltAtEnd = haltAtEnd;
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
|
||||
ml::leds.ActiveSlotProcessing();
|
||||
if (ms::selector.MoveToSlot(mg::globals.ActiveSlot()) != ms::Selector::OperationResult::Accepted) {
|
||||
// We can't get any FINDA readings if the selector is at the wrong spot - move it accordingly if necessary
|
||||
// And prevent issuing any commands to the idler in such an error state
|
||||
|
|
@ -44,14 +44,14 @@ bool FeedToFinda::Step() {
|
|||
// mpu::pulley.PlanMove(config::filamentMinLoadedToMMU, config::pulleySlowFeedrate);
|
||||
// }
|
||||
|
||||
mpu::pulley.PlanMove(config::maximumFeedToFinda, config::pulleySlowFeedrate);
|
||||
mpu::pulley.PlanMove(config::maximumFeedToFinda, mg::globals.PulleySlowFeedrate_mm_s());
|
||||
if (feedPhaseLimited) {
|
||||
state = PushingFilament;
|
||||
} else {
|
||||
state = PushingFilamentUnlimited;
|
||||
// in unlimited move we plan 2 moves at once to make the move "seamless"
|
||||
// one move has already been planned above, this is the second one
|
||||
mpu::pulley.PlanMove(config::maximumFeedToFinda, config::pulleySlowFeedrate);
|
||||
mpu::pulley.PlanMove(config::maximumFeedToFinda, mg::globals.PulleySlowFeedrate_mm_s());
|
||||
}
|
||||
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InSelector);
|
||||
mui::userInput.Clear(); // remove all buffered events if any just before we wait for some input
|
||||
|
|
@ -68,7 +68,7 @@ bool FeedToFinda::Step() {
|
|||
return true; // return immediately to allow for a seamless planning of another move (like feeding to bondtech)
|
||||
} else if (mm::motion.QueueEmpty()) { // all moves have been finished and FINDA didn't switch on
|
||||
state = Failed;
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::blink0);
|
||||
ml::leds.ActiveSlotError();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
@ -85,7 +85,7 @@ bool FeedToFinda::Step() {
|
|||
return true; // return immediately to allow for a seamless planning of another move (like feeding to bondtech)
|
||||
} else if (mm::motion.PlannedMoves(mm::Pulley) < 2) {
|
||||
// plan another move to make the illusion of unlimited moves
|
||||
mpu::pulley.PlanMove(config::maximumFeedToFinda, config::pulleySlowFeedrate);
|
||||
mpu::pulley.PlanMove(config::maximumFeedToFinda, mg::globals.PulleySlowFeedrate_mm_s());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace logic {
|
|||
/// Leaves the Pulley axis enabled for chaining potential next operations
|
||||
struct FeedToFinda {
|
||||
/// internal states of the state machine
|
||||
enum {
|
||||
enum : uint8_t {
|
||||
EngagingIdler,
|
||||
PushingFilament,
|
||||
PushingFilamentUnlimited,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ bool HWSanity::Reset(uint8_t param) {
|
|||
return true;
|
||||
}
|
||||
|
||||
enum pin_bits {
|
||||
enum pin_bits : uint8_t {
|
||||
BIT_STEP = 0b001,
|
||||
BIT_DIR = 0b010,
|
||||
BIT_ENA = 0b100,
|
||||
|
|
|
|||
|
|
@ -50,8 +50,6 @@ void logic::LoadFilament::Reset2(bool feedPhaseLimited) {
|
|||
if (!feed.Reset(feedPhaseLimited, true)) {
|
||||
// selector refused to move
|
||||
GoToErrDisengagingIdler(ErrorCode::FINDA_FLICKERS);
|
||||
} else {
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
/// therefore the error codes have been extracted to one place.
|
||||
///
|
||||
/// Please note that currently only LoadFilament can return something else than "OK"
|
||||
enum class ResultCode : uint_fast16_t {
|
||||
enum class ResultCode : uint_fast8_t {
|
||||
OK = 0,
|
||||
Cancelled = 1
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace logic {
|
|||
void RetractFromFinda::Reset() {
|
||||
dbg_logic_P(PSTR("\nRetract from FINDA\n\n"));
|
||||
state = EngagingIdler;
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
|
||||
ml::leds.ActiveSlotProcessing();
|
||||
mi::idler.Engage(mg::globals.ActiveSlot());
|
||||
}
|
||||
|
||||
|
|
@ -33,10 +33,10 @@ bool RetractFromFinda::Step() {
|
|||
state = OK;
|
||||
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::AtPulley);
|
||||
dbg_logic_fP(PSTR("Pulley end steps %u"), mpu::pulley.CurrentPosition_mm());
|
||||
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
|
||||
ml::leds.ActiveSlotDoneEmpty();
|
||||
} else { // FINDA didn't switch off
|
||||
state = Failed;
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::blink0);
|
||||
ml::leds.ActiveSlotError();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace logic {
|
|||
/// - leaves idler engaged for chaining operations
|
||||
struct RetractFromFinda {
|
||||
/// internal states of the state machine
|
||||
enum {
|
||||
enum : uint8_t {
|
||||
EngagingIdler,
|
||||
UnloadBackToPTFE,
|
||||
OK,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ bool ToolChange::Reset(uint8_t param) {
|
|||
if (mg::globals.FilamentLoaded() >= mg::FilamentLoadState::InSelector) {
|
||||
dbg_logic_P(PSTR("Filament is loaded --> unload"));
|
||||
state = ProgressCode::UnloadingFilament;
|
||||
unl.Reset(mg::globals.ActiveSlot());
|
||||
unl.Reset2(mg::globals.ActiveSlot());
|
||||
} else {
|
||||
mg::globals.SetFilamentLoaded(plannedSlot, mg::FilamentLoadState::InSelector); // activate the correct slot, feed uses that
|
||||
if (feed.Reset(true, false)) {
|
||||
|
|
@ -54,20 +54,19 @@ bool ToolChange::Reset(uint8_t param) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void logic::ToolChange::GoToFeedingToBondtech() {
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
|
||||
void ToolChange::GoToFeedingToBondtech() {
|
||||
james.Reset(3);
|
||||
state = ProgressCode::FeedingToBondtech;
|
||||
error = ErrorCode::RUNNING;
|
||||
}
|
||||
|
||||
void logic::ToolChange::ToolChangeFinishedCorrectly() {
|
||||
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::on, ml::off);
|
||||
void ToolChange::ToolChangeFinishedCorrectly() {
|
||||
ml::leds.ActiveSlotDonePrimed();
|
||||
mui::userInput.SetPrinterInCharge(false);
|
||||
FinishedOK();
|
||||
}
|
||||
|
||||
void logic::ToolChange::GoToFeedingToFinda() {
|
||||
void ToolChange::GoToFeedingToFinda() {
|
||||
state = ProgressCode::FeedingToFinda;
|
||||
error = ErrorCode::RUNNING;
|
||||
mg::globals.SetFilamentLoaded(plannedSlot, mg::FilamentLoadState::AtPulley);
|
||||
|
|
|
|||
|
|
@ -28,11 +28,17 @@ bool UnloadFilament::Reset(uint8_t /*param*/) {
|
|||
mpu::pulley.InitAxis();
|
||||
state = ProgressCode::UnloadingToFinda;
|
||||
error = ErrorCode::RUNNING;
|
||||
skipDisengagingIdler = false;
|
||||
unl.Reset(maxRetries);
|
||||
ml::leds.SetAllOff();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnloadFilament::Reset2(uint8_t param) {
|
||||
bool rv = Reset(param);
|
||||
skipDisengagingIdler = true;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void UnloadFilament::UnloadFinishedCorrectly() {
|
||||
FinishedOK();
|
||||
mpu::pulley.Disable();
|
||||
|
|
@ -77,7 +83,11 @@ bool UnloadFilament::StepInner() {
|
|||
GoToErrDisengagingIdler(ErrorCode::FINDA_DIDNT_SWITCH_OFF); // signal unloading error
|
||||
} else {
|
||||
state = ProgressCode::DisengagingIdler;
|
||||
mi::idler.Disengage();
|
||||
if (skipDisengagingIdler && ms::selector.State() == ms::Selector::Ready) {
|
||||
UnloadFinishedCorrectly(); // skip disengaging the Idler - to be used inside ToolChange to speed up
|
||||
} else {
|
||||
mi::idler.Disengage();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -12,12 +12,15 @@ namespace logic {
|
|||
class UnloadFilament : public CommandBase {
|
||||
public:
|
||||
inline constexpr UnloadFilament()
|
||||
: CommandBase() {}
|
||||
: CommandBase()
|
||||
, skipDisengagingIdler(false) {}
|
||||
|
||||
/// Restart the automaton
|
||||
/// @param param is not used, always unloads from the active slot
|
||||
bool Reset(uint8_t param) override;
|
||||
|
||||
bool Reset2(uint8_t param);
|
||||
|
||||
/// @returns true if the state machine finished its job, false otherwise
|
||||
bool StepInner() override;
|
||||
|
||||
|
|
@ -32,6 +35,7 @@ private:
|
|||
UnloadToFinda unl;
|
||||
FeedToFinda feed;
|
||||
RetractFromFinda retract;
|
||||
bool skipDisengagingIdler;
|
||||
};
|
||||
|
||||
/// The one and only instance of UnloadFilament state machine in the FW
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "../modules/motion.h"
|
||||
#include "../modules/permanent_storage.h"
|
||||
#include "../modules/pulley.h"
|
||||
#include "../modules/timebase.h"
|
||||
|
||||
namespace logic {
|
||||
|
||||
|
|
@ -19,19 +20,40 @@ void UnloadToFinda::Reset(uint8_t maxTries) {
|
|||
} else {
|
||||
// FINDA is sensing the filament, plan moves to unload it
|
||||
state = EngagingIdler;
|
||||
mi::idler.Engage(mg::globals.ActiveSlot());
|
||||
mi::idler.PartiallyDisengage(mg::globals.ActiveSlot()); // basically prepare before the active slot - saves ~1s
|
||||
started_ms = mt::timebase.Millis();
|
||||
ml::leds.ActiveSlotProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
bool UnloadToFinda::Step() {
|
||||
switch (state) {
|
||||
// start by engaging the idler into intermediate position
|
||||
// Then, wait for !fsensor.Pressed: that's to speed-up the pull process - unload operation will be started during the purging moves
|
||||
// and as soon as the fsensor turns off, the MMU engages the idler fully and starts pulling.
|
||||
// It will not wait for the extruder to finish the relieve move.
|
||||
// However, such an approach breaks running the MMU on a non-reworked MK4/C1, which hasn't been officially supported, but possible (with some level of uncertainity).
|
||||
case EngagingIdler:
|
||||
if (mg::globals.FilamentLoaded() >= mg::FilamentLoadState::InSelector) {
|
||||
state = UnloadingToFinda;
|
||||
mpu::pulley.InitAxis();
|
||||
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::blink0);
|
||||
if (!mi::idler.PartiallyDisengaged()) { // just waiting for Idler to get into the target intermediate position
|
||||
return false;
|
||||
}
|
||||
if (mfs::fsensor.Pressed()) { // still pressed, printer didn't free the filament yet
|
||||
if (mt::timebase.Elapsed(started_ms, 4000)) {
|
||||
state = FailedFSensor; // fsensor didn't turn off within 4 seconds, something is seriously wrong
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
state = FailedFINDA;
|
||||
// fsensor is OFF and Idler is partially engaged, engage the Idler fully and pull
|
||||
if (mg::globals.FilamentLoaded() >= mg::FilamentLoadState::InSelector) {
|
||||
state = UnloadingToFinda;
|
||||
mpu::pulley.InitAxis();
|
||||
mi::idler.Engage(mg::globals.ActiveSlot());
|
||||
|
||||
// slow move for the first few millimeters - help the printer relieve the filament while engaging the Idler fully
|
||||
mpu::pulley.PlanMove(-config::fsensorToNozzleAvoidGrindUnload, mg::globals.PulleySlowFeedrate_mm_s(), mg::globals.PulleySlowFeedrate_mm_s());
|
||||
} else {
|
||||
state = FailedFINDA;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case UnloadingToFinda:
|
||||
|
|
@ -55,17 +77,20 @@ bool UnloadToFinda::Step() {
|
|||
// This scenario should not be tried again - repeating it may cause more damage to filament + potentially more collateral damage
|
||||
state = FailedFSensor;
|
||||
mm::motion.AbortPlannedMoves(); // stop rotating the pulley
|
||||
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
|
||||
ml::leds.ActiveSlotDoneEmpty();
|
||||
} else if (!mf::finda.Pressed()) {
|
||||
// detected end of filament
|
||||
state = OK;
|
||||
mm::motion.AbortPlannedMoves(); // stop rotating the pulley
|
||||
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
|
||||
ml::leds.ActiveSlotDoneEmpty();
|
||||
} else if (/*tmc2130_read_gstat() &&*/ mm::motion.QueueEmpty()) {
|
||||
// we reached the end of move queue, but the FINDA didn't switch off
|
||||
// two possible causes - grinded filament or malfunctioning FINDA
|
||||
if (--maxTries) {
|
||||
Reset(maxTries); // try again
|
||||
// Ideally, the Idler shall rehome and then try again.
|
||||
// That would auto-resolve errors caused by slipped or misaligned Idler
|
||||
mi::idler.InvalidateHoming();
|
||||
Reset(maxTries);
|
||||
} else {
|
||||
state = FailedFINDA;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace logic {
|
|||
/// - load/unload to finda
|
||||
struct UnloadToFinda {
|
||||
/// internal states of the state machine
|
||||
enum {
|
||||
enum : uint8_t {
|
||||
EngagingIdler,
|
||||
UnloadingToFinda,
|
||||
WaitingForFINDA,
|
||||
|
|
@ -24,7 +24,8 @@ struct UnloadToFinda {
|
|||
inline constexpr UnloadToFinda()
|
||||
: state(OK)
|
||||
, maxTries(3)
|
||||
, unloadStart_mm(0) {}
|
||||
, unloadStart_mm(0)
|
||||
, started_ms(0) {}
|
||||
|
||||
/// Restart the automaton
|
||||
/// @param maxTries maximum number of retried attempts before reporting a fail
|
||||
|
|
@ -40,6 +41,7 @@ private:
|
|||
uint8_t state;
|
||||
uint8_t maxTries;
|
||||
int32_t unloadStart_mm; // intentionally trying to avoid using U_mm because it is a float (reps. long double)
|
||||
uint16_t started_ms; // timeout on fsensor turn off
|
||||
};
|
||||
|
||||
} // namespace logic
|
||||
|
|
|
|||
|
|
@ -95,10 +95,7 @@ static void setup2() {
|
|||
mb::buttons.Step();
|
||||
|
||||
// Turn off all leds
|
||||
for (uint8_t i = 0; i < config::toolCount; i++) {
|
||||
ml::leds.SetMode(i, ml::green, ml::off);
|
||||
ml::leds.SetMode(i, ml::red, ml::off);
|
||||
}
|
||||
ml::leds.SetAllOff();
|
||||
ml::leds.Step();
|
||||
mb::buttons.Step();
|
||||
|
||||
|
|
@ -128,7 +125,7 @@ static void setup2() {
|
|||
|
||||
// activate the correct LED if filament is present
|
||||
if (mg::globals.FilamentLoaded() > mg::FilamentLoadState::AtPulley) {
|
||||
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on);
|
||||
ml::leds.ActiveSlotDonePrimed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,12 +94,13 @@ constexpr AxisUnit<T, A, U> operator*(const long double f, const AxisUnit<T, A,
|
|||
struct AxisScale {
|
||||
unit::UnitBase base;
|
||||
long double stepsPerUnit;
|
||||
long double stepsPerUnitReciprocal;
|
||||
};
|
||||
|
||||
static constexpr AxisScale axisScale[config::NUM_AXIS] = {
|
||||
{ config::pulleyLimits.base, config::pulley.stepsPerUnit },
|
||||
{ config::selectorLimits.base, config::selector.stepsPerUnit },
|
||||
{ config::idlerLimits.base, config::idler.stepsPerUnit },
|
||||
{ config::pulleyLimits.base, config::pulley.stepsPerUnit, config::pulley.stepsPerUnitReciprocal },
|
||||
{ config::selectorLimits.base, config::selector.stepsPerUnit, config::selector.stepsPerUnitReciprocal },
|
||||
{ config::idlerLimits.base, config::idler.stepsPerUnit, config::idler.stepsPerUnitReciprocal },
|
||||
};
|
||||
|
||||
/// Convert a unit::Unit to AxisUnit.
|
||||
|
|
@ -126,7 +127,7 @@ template <typename U, typename AU, typename T = int32_t>
|
|||
static constexpr T axisUnitToTruncatedUnit(AU v, long double mul = 1.) {
|
||||
static_assert(AU::unit == U::unit, "incorrect unit type conversion");
|
||||
static_assert(U::base == axisScale[AU::axis].base, "incorrect unit base conversion");
|
||||
return { ((T)v.v / (T)(axisScale[AU::axis].stepsPerUnit / mul)) };
|
||||
return { ((T)(v.v * (axisScale[AU::axis].stepsPerUnitReciprocal / mul))) };
|
||||
}
|
||||
|
||||
/// Truncate an Unit type to an integer (normally int32_t)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ private:
|
|||
};
|
||||
|
||||
/// Enum of buttons - used also as indices in an array of buttons to keep the code size tight.
|
||||
enum {
|
||||
enum : uint8_t {
|
||||
Right = 0,
|
||||
Middle,
|
||||
Left
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ private:
|
|||
/// Intentionally not modeled as an enum class
|
||||
/// as it would impose additional casts which do not play well with the struct Flags
|
||||
/// and would make the code less readable
|
||||
enum State {
|
||||
enum State : uint8_t {
|
||||
Waiting = 0,
|
||||
Detected,
|
||||
WaitForRelease,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ void Idler::PlanHomingMoveBack() {
|
|||
bool Idler::FinishHomingAndPlanMoveToParkPos() {
|
||||
// check the axis' length
|
||||
if (AxisDistance(mm::axisUnitToTruncatedUnit<config::U_deg>(mm::motion.CurPosition<mm::Idler>()))
|
||||
< (config::idlerLimits.lenght.v - 10)) { //@@TODO is 10 degrees ok?
|
||||
< uint16_t(config::idlerLimits.lenght.v - 10)) { //@@TODO is 10 degrees ok?
|
||||
return false; // we couldn't home correctly, we cannot set the Idler's position
|
||||
}
|
||||
|
||||
|
|
@ -64,32 +64,12 @@ void Idler::FinishMove() {
|
|||
}
|
||||
|
||||
bool Idler::StallGuardAllowed(bool forward) const {
|
||||
const uint8_t checkDistance = forward ? 220 : 200;
|
||||
const uint8_t checkDistance = forward ? 200 : 180;
|
||||
return AxisDistance(mm::axisUnitToTruncatedUnit<config::U_deg>(mm::motion.CurPosition<mm::Idler>())) > checkDistance;
|
||||
}
|
||||
|
||||
Idler::OperationResult Idler::Disengage() {
|
||||
if (state == Moving || IsOnHold()) {
|
||||
dbg_logic_P(PSTR("Moving --> Disengage refused"));
|
||||
return OperationResult::Refused;
|
||||
}
|
||||
plannedSlot = IdleSlotIndex();
|
||||
plannedMove = Operation::disengage;
|
||||
|
||||
// coordinates invalid, first home, then disengage
|
||||
if (!homingValid) {
|
||||
PlanHome();
|
||||
return OperationResult::Accepted;
|
||||
}
|
||||
|
||||
// already disengaged
|
||||
if (Disengaged()) {
|
||||
dbg_logic_P(PSTR("Idler Disengaged"));
|
||||
return OperationResult::Accepted;
|
||||
}
|
||||
|
||||
// disengaging
|
||||
return InitMovementNoReinitAxis();
|
||||
return PlanMoveInner(IdleSlotIndex(), Operation::disengage);
|
||||
}
|
||||
|
||||
Idler::OperationResult Idler::PartiallyDisengage(uint8_t slot) {
|
||||
|
|
@ -114,7 +94,7 @@ Idler::OperationResult Idler::PlanMoveInner(uint8_t slot, Operation plannedOp) {
|
|||
return OperationResult::Accepted;
|
||||
}
|
||||
|
||||
// coordinates invalid, first home, then engage
|
||||
// coordinates invalid, first home, then engage or disengage
|
||||
// The MMU FW only decides to engage the Idler when it is supposed to do something and not while it is idle
|
||||
// so rebooting the MMU while the printer is printing (and thus holding the filament by the moving Idler)
|
||||
// should not be an issue
|
||||
|
|
@ -123,8 +103,8 @@ Idler::OperationResult Idler::PlanMoveInner(uint8_t slot, Operation plannedOp) {
|
|||
return OperationResult::Accepted;
|
||||
}
|
||||
|
||||
// already engaged
|
||||
if (currentlyEngaged == plannedMove) {
|
||||
// already engaged or disengaged
|
||||
if (currentlyEngaged == plannedMove && currentSlot == plannedSlot) {
|
||||
return OperationResult::Accepted;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "leds.h"
|
||||
#include "../hal/shr16.h"
|
||||
#include "timebase.h"
|
||||
#include "globals.h"
|
||||
|
||||
namespace modules {
|
||||
namespace leds {
|
||||
|
|
@ -68,5 +69,21 @@ void LEDs::SetAllOff() {
|
|||
}
|
||||
}
|
||||
|
||||
void LEDs::ActiveSlotProcessing() {
|
||||
SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
|
||||
}
|
||||
|
||||
void LEDs::ActiveSlotError() {
|
||||
SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::blink0);
|
||||
}
|
||||
|
||||
void LEDs::ActiveSlotDoneEmpty() {
|
||||
SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::off);
|
||||
}
|
||||
|
||||
void LEDs::ActiveSlotDonePrimed() {
|
||||
SetPairButOffOthers(mg::globals.ActiveSlot(), ml::on, ml::off);
|
||||
}
|
||||
|
||||
} // namespace leds
|
||||
} // namespace modules
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace leds {
|
|||
|
||||
/// Enum of LED modes
|
||||
/// blink0 and blink1 allow for interlaced blinking of LEDs (one is on and the other off)
|
||||
enum Mode {
|
||||
enum Mode : uint8_t {
|
||||
off,
|
||||
on,
|
||||
blink0, ///< start blinking at even periods
|
||||
|
|
@ -28,7 +28,7 @@ enum Mode {
|
|||
};
|
||||
|
||||
/// Enum of LEDs color - green or red
|
||||
enum Color {
|
||||
enum Color : uint8_t {
|
||||
red = 0,
|
||||
green = 1
|
||||
};
|
||||
|
|
@ -122,6 +122,14 @@ public:
|
|||
/// Turn off all LEDs
|
||||
void SetAllOff();
|
||||
|
||||
/// Convenience functions - provide uniform implementation of LED behaviour through all the logic commands.
|
||||
/// Intentionally not inlined to save quite some space (140B)
|
||||
/// It's not a clean solution, LEDs should not know about mg::globals.ActiveSlot(), but the savings are important
|
||||
void ActiveSlotProcessing();
|
||||
void ActiveSlotError();
|
||||
void ActiveSlotDoneEmpty();
|
||||
void ActiveSlotDonePrimed();
|
||||
|
||||
private:
|
||||
constexpr static const uint8_t ledPairs = config::toolCount;
|
||||
/// pairs of LEDs:
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace motion {
|
|||
class MovableBase {
|
||||
public:
|
||||
/// Internal states of the state machine
|
||||
enum {
|
||||
enum : uint8_t {
|
||||
Ready = 0, // intentionally set as zero in order to allow zeroing the Idler structure upon startup -> avoid explicit initialization code
|
||||
Moving = 1,
|
||||
PlannedHome = 2,
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public:
|
|||
static bool set(uint8_t filament);
|
||||
|
||||
private:
|
||||
enum Key {
|
||||
enum Key : uint8_t {
|
||||
KeyFront1,
|
||||
KeyReverse1,
|
||||
KeyFront2,
|
||||
|
|
|
|||
|
|
@ -11,11 +11,6 @@ namespace pulley {
|
|||
|
||||
Pulley pulley;
|
||||
|
||||
bool __attribute__((noinline)) Pulley::FinishHomingAndPlanMoveToParkPos() {
|
||||
mm::motion.SetPosition(mm::Pulley, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pulley::Step() {
|
||||
if (IsOnHold()) {
|
||||
return true; // just wait, do nothing!
|
||||
|
|
@ -28,10 +23,6 @@ bool Pulley::Step() {
|
|||
case Moving:
|
||||
PerformMove();
|
||||
return false;
|
||||
case HomeBack:
|
||||
homingValid = true;
|
||||
FinishHomingAndPlanMoveToParkPos();
|
||||
return true;
|
||||
case Ready:
|
||||
return true;
|
||||
case TMCFailed:
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ protected:
|
|||
virtual void PrepareMoveToPlannedSlot() override {}
|
||||
virtual void PlanHomingMoveForward() override {}
|
||||
virtual void PlanHomingMoveBack() override {}
|
||||
virtual bool FinishHomingAndPlanMoveToParkPos() override;
|
||||
virtual bool FinishHomingAndPlanMoveToParkPos() override { return true; }
|
||||
virtual void FinishMove() override {}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ void Selector::PlanHomingMoveBack() {
|
|||
|
||||
bool Selector::FinishHomingAndPlanMoveToParkPos() {
|
||||
// check the axis' length
|
||||
if (AxisDistance(mm::axisUnitToTruncatedUnit<config::U_mm>(mm::motion.CurPosition<mm::Selector>())) < (config::selectorLimits.lenght.v - 3)) { //@@TODO is 3mm ok?
|
||||
if (AxisDistance(mm::axisUnitToTruncatedUnit<config::U_mm>(mm::motion.CurPosition<mm::Selector>())) < uint16_t(config::selectorLimits.lenght.v - 3)) { //@@TODO is 3mm ok?
|
||||
return false; // we couldn't home correctly, we cannot set the Selector's position
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface = {
|
|||
.Address = CDC_TX_EPADDR,
|
||||
.Size = CDC_TXRX_EPSIZE,
|
||||
.Type = EP_TYPE_BULK,
|
||||
.Banks = 2,
|
||||
.Banks = 1,
|
||||
},
|
||||
.DataOUTEndpoint = {
|
||||
.Address = CDC_RX_EPADDR,
|
||||
.Size = CDC_TXRX_EPSIZE,
|
||||
.Type = EP_TYPE_BULK,
|
||||
.Banks = 2,
|
||||
.Banks = 1,
|
||||
},
|
||||
.NotificationEndpoint = {
|
||||
.Address = CDC_NOTIFICATION_EPADDR,
|
||||
|
|
@ -90,6 +90,14 @@ namespace usb {
|
|||
CDC cdc;
|
||||
|
||||
void CDC::Init() {
|
||||
#if defined(USE_STATIC_OPTIONS) && (USE_STATIC_OPTIONS & USB_OPT_MANUAL_PLL)
|
||||
#if defined(USB_SERIES_4_AVR)
|
||||
PLLFRQ = ((1 << PLLUSB) | (1 << PDIV3) | (1 << PDIV1));
|
||||
#endif
|
||||
USB_PLL_On();
|
||||
while (!(USB_PLL_IsReady()));
|
||||
#endif
|
||||
|
||||
USB_Init();
|
||||
|
||||
/* Create a regular character stream for the interface so that it can be used with the stdio.h functions */
|
||||
|
|
|
|||
38
src/pins.h
38
src/pins.h
|
|
@ -3,28 +3,28 @@
|
|||
#include "hal/gpio.h"
|
||||
|
||||
/// pin definitions
|
||||
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_MISO_PIN = { GPIOB, 3 };
|
||||
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_MOSI_PIN = { GPIOB, 2 };
|
||||
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_SCK_PIN = { GPIOB, 1 };
|
||||
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_SS_PIN = { GPIOB, 0 };
|
||||
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_MISO_PIN = { GPIOB, (1 << 3) };
|
||||
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_MOSI_PIN = { GPIOB, (1 << 2) };
|
||||
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_SCK_PIN = { GPIOB, (1 << 1) };
|
||||
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_SS_PIN = { GPIOB, (1 << 0) };
|
||||
|
||||
static constexpr hal::gpio::GPIO_pin SHR16_DATA = { GPIOB, 5 }; ///DS
|
||||
static constexpr hal::gpio::GPIO_pin SHR16_LATCH = { GPIOB, 6 }; ///STCP
|
||||
static constexpr hal::gpio::GPIO_pin SHR16_CLOCK = { GPIOC, 7 }; ///SHCP
|
||||
static constexpr hal::gpio::GPIO_pin SHR16_DATA = { GPIOB, (1 << 5) }; ///DS
|
||||
static constexpr hal::gpio::GPIO_pin SHR16_LATCH = { GPIOB, (1 << 6) }; ///STCP
|
||||
static constexpr hal::gpio::GPIO_pin SHR16_CLOCK = { GPIOC, (1 << 7) }; ///SHCP
|
||||
|
||||
static constexpr hal::gpio::GPIO_pin USART_RX = { GPIOD, 2 };
|
||||
static constexpr hal::gpio::GPIO_pin USART_TX = { GPIOD, 3 };
|
||||
static constexpr hal::gpio::GPIO_pin USART_RX = { GPIOD, (1 << 2) };
|
||||
static constexpr hal::gpio::GPIO_pin USART_TX = { GPIOD, (1 << 3) };
|
||||
|
||||
static constexpr hal::gpio::GPIO_pin PULLEY_CS_PIN = { GPIOC, 6 };
|
||||
static constexpr hal::gpio::GPIO_pin PULLEY_SG_PIN = { GPIOF, 4 };
|
||||
static constexpr hal::gpio::GPIO_pin PULLEY_STEP_PIN = { GPIOB, 4 };
|
||||
static constexpr hal::gpio::GPIO_pin PULLEY_CS_PIN = { GPIOC, (1 << 6) };
|
||||
static constexpr hal::gpio::GPIO_pin PULLEY_SG_PIN = { GPIOF, (1 << 4) };
|
||||
static constexpr hal::gpio::GPIO_pin PULLEY_STEP_PIN = { GPIOB, (1 << 4) };
|
||||
|
||||
static constexpr hal::gpio::GPIO_pin SELECTOR_CS_PIN = { GPIOD, 7 };
|
||||
static constexpr hal::gpio::GPIO_pin SELECTOR_SG_PIN = { GPIOF, 1 };
|
||||
static constexpr hal::gpio::GPIO_pin SELECTOR_STEP_PIN = { GPIOD, 4 };
|
||||
static constexpr hal::gpio::GPIO_pin SELECTOR_CS_PIN = { GPIOD, (1 << 7) };
|
||||
static constexpr hal::gpio::GPIO_pin SELECTOR_SG_PIN = { GPIOF, (1 << 1) };
|
||||
static constexpr hal::gpio::GPIO_pin SELECTOR_STEP_PIN = { GPIOD, (1 << 4) };
|
||||
|
||||
static constexpr hal::gpio::GPIO_pin IDLER_CS_PIN = { GPIOB, 7 };
|
||||
static constexpr hal::gpio::GPIO_pin IDLER_SG_PIN = { GPIOF, 0 };
|
||||
static constexpr hal::gpio::GPIO_pin IDLER_STEP_PIN = { GPIOD, 6 };
|
||||
static constexpr hal::gpio::GPIO_pin IDLER_CS_PIN = { GPIOB, (1 << 7) };
|
||||
static constexpr hal::gpio::GPIO_pin IDLER_SG_PIN = { GPIOF, (1 << 0) };
|
||||
static constexpr hal::gpio::GPIO_pin IDLER_STEP_PIN = { GPIOD, (1 << 6) };
|
||||
|
||||
static constexpr hal::gpio::GPIO_pin FINDA_PIN = { GPIOF, 6 }; /// PF6 A1 ADC6/TDI
|
||||
static constexpr hal::gpio::GPIO_pin FINDA_PIN = { GPIOF, (1 << 6) }; /// PF6 A1 ADC6/TDI
|
||||
|
|
|
|||
|
|
@ -145,9 +145,9 @@
|
|||
| 0x0bh 11 | uint8 | extra_load_distance | 00h 0 | 1eh 30 | unit mm | Read / Write | M707 A0x0b | M708 A0x0b Xnn
|
||||
| 0x0ch 12 | uint8 | FSensor_unload_check_dist. | 00h 0 | 28h 30 | unit mm | Read / Write | M707 A0x0c | M708 A0x0c Xnn
|
||||
| 0x0dh 13 | uint16 | Pulley_unload_feedrate | 0000h 0 | 005fh 95 | unit mm/s | Read / Write | M707 A0x0d | M708 A0x0d Xnnnn
|
||||
| 0x0eh 14 | uint16 | Pulley_acceleration | 0000h 0 | 320h 800.0 | unit mm/s² | Read (Write) | M707 A0x0e | (M708 A0x0e Xnnnn)
|
||||
| 0x0fh 15 | uint16 | Selector_acceleration | 0000h 0 | 00c8h 200.0 | unit mm/s² | Read (Write) | M707 A0x0f | (M708 A0x0f Xnnnn)
|
||||
| 0x10h 16 | uint16 | Idler_acceleration | 0000h 0 | 01f4h 500.0 | unit deg/s² | Read (Write) | M707 A0x10 | (M708 A0x10 Xnnnn)
|
||||
| 0x0eh 14 | uint16 | Pulley_acceleration | 0000h 0 | 320h 800.0 | unit mm/s² | Read / Write | M707 A0x0e | M708 A0x0e Xnnnn
|
||||
| 0x0fh 15 | uint16 | Selector_acceleration | 0000h 0 | 00c8h 200.0 | unit mm/s² | Read / Write | M707 A0x0f | M708 A0x0f Xnnnn
|
||||
| 0x10h 16 | uint16 | Idler_acceleration | 0000h 0 | 01f4h 500.0 | unit deg/s² | Read / Write | M707 A0x10 | M708 A0x10 Xnnnn
|
||||
| 0x11h 17 | uint16 | Pulley_load_feedrate | 0000h 0 | 005fh 95 | unit mm/s | Read / Write | M707 A0x11 | M708 A0x11 Xnnnn
|
||||
| 0x12h 18 | uint16 | Selector_nominal_feedrate | 0000h 0 | 002dh 45 | unit mm/s | Read / Write | M707 A0x12 | M708 A0x12 Xnnnn
|
||||
| 0x13h 19 | uint16 | Idler_nominal_feedrate | 0000h 0 | 012ch 300 | unit deg/s | Read / Write | M707 A0x13 | M708 A0x13 Xnnnn
|
||||
|
|
@ -169,44 +169,6 @@
|
|||
| 0x23h 35 | uint8 | Cut length | 0-255 | 8 | unit mm | Read / Write | M707 A0x23 | M708 A0x23 Xn
|
||||
*/
|
||||
|
||||
struct __attribute__((packed)) RegisterFlags {
|
||||
struct __attribute__((packed)) A {
|
||||
uint8_t size : 2; // 0: 1 bit, 1: 1 byte, 2: 2 bytes - keeping size as the lowest 2 bits avoids costly shifts when accessing them
|
||||
uint8_t writable : 1;
|
||||
uint8_t rwfuncs : 1; // 1: register needs special read and write functions
|
||||
constexpr A(uint8_t size, bool writable)
|
||||
: size(size)
|
||||
, writable(writable)
|
||||
, rwfuncs(0) {}
|
||||
constexpr A(uint8_t size, bool writable, bool rwfuncs)
|
||||
: size(size)
|
||||
, writable(writable)
|
||||
, rwfuncs(rwfuncs) {}
|
||||
};
|
||||
union __attribute__((packed)) U {
|
||||
A bits;
|
||||
uint8_t b;
|
||||
constexpr U(uint8_t size, bool writable)
|
||||
: bits(size, writable) {}
|
||||
constexpr U(uint8_t size, bool writable, bool rwfuncs)
|
||||
: bits(size, writable, rwfuncs) {}
|
||||
constexpr U(uint8_t b)
|
||||
: b(b) {}
|
||||
} u;
|
||||
constexpr RegisterFlags(uint8_t size, bool writable)
|
||||
: u(size, writable) {}
|
||||
constexpr RegisterFlags(uint8_t size, bool writable, bool rwfuncs)
|
||||
: u(size, writable, rwfuncs) {}
|
||||
explicit constexpr RegisterFlags(uint8_t b)
|
||||
: u(b) {}
|
||||
|
||||
constexpr bool Writable() const { return u.bits.writable; }
|
||||
constexpr bool RWFuncs() const { return u.bits.rwfuncs; }
|
||||
constexpr uint8_t Size() const { return u.bits.size; }
|
||||
};
|
||||
|
||||
static_assert(sizeof(RegisterFlags) == 1);
|
||||
|
||||
using TReadFunc = uint16_t (*)();
|
||||
using TWriteFunc = void (*)(uint16_t);
|
||||
|
||||
|
|
@ -214,7 +176,6 @@ using TWriteFunc = void (*)(uint16_t);
|
|||
static constexpr uint16_t dummyZero = 0;
|
||||
|
||||
struct __attribute__((packed)) RegisterRec {
|
||||
RegisterFlags flags;
|
||||
union __attribute__((packed)) U1 {
|
||||
void *addr;
|
||||
TReadFunc readFunc;
|
||||
|
|
@ -233,29 +194,22 @@ struct __attribute__((packed)) RegisterRec {
|
|||
: addr(a) {}
|
||||
} A2;
|
||||
|
||||
template <typename T>
|
||||
constexpr RegisterRec(bool writable, T *address)
|
||||
: flags(RegisterFlags(sizeof(T), writable))
|
||||
, A1((void *)address)
|
||||
, A2((void *)nullptr) {}
|
||||
constexpr RegisterRec(const TReadFunc &readFunc, uint8_t bytes)
|
||||
: flags(RegisterFlags(bytes, false, true))
|
||||
, A1(readFunc)
|
||||
constexpr RegisterRec(const TReadFunc &readFunc)
|
||||
: A1(readFunc)
|
||||
, A2((void *)nullptr) {}
|
||||
|
||||
constexpr RegisterRec(const TReadFunc &readFunc, const TWriteFunc &writeFunc, uint8_t bytes)
|
||||
: flags(RegisterFlags(bytes, true, true))
|
||||
, A1(readFunc)
|
||||
constexpr RegisterRec(const TReadFunc &readFunc, const TWriteFunc &writeFunc)
|
||||
: A1(readFunc)
|
||||
, A2(writeFunc) {}
|
||||
|
||||
constexpr RegisterRec()
|
||||
: flags(RegisterFlags(1, false, false))
|
||||
, A1((void *)&dummyZero)
|
||||
: A1((void *)&dummyZero)
|
||||
, A2((void *)nullptr) {}
|
||||
};
|
||||
|
||||
// Make sure the structure is tightly packed - necessary for unit tests.
|
||||
static_assert(sizeof(RegisterRec) == sizeof(uint8_t) + sizeof(void *) + sizeof(void *));
|
||||
static_assert(sizeof(RegisterRec) == /*sizeof(uint8_t) +*/ sizeof(void *) + sizeof(void *));
|
||||
|
||||
// Beware: the size is expected to be 17B on an x86_64 and it requires the platform to be able to do unaligned reads.
|
||||
// That might be a problem when running unit tests on non-x86 platforms.
|
||||
// So far, no countermeasures have been taken to tackle this potential issue.
|
||||
|
|
@ -274,176 +228,157 @@ static_assert(sizeof(RegisterRec) == sizeof(uint8_t) + sizeof(void *) + sizeof(v
|
|||
// sts <modules::globals::globals+0x4>, r24
|
||||
// ret
|
||||
//
|
||||
// @@TODO at the moment we are having problems compiling this array statically into PROGMEM.
|
||||
// In this project that's really not an issue since we have half of the RAM empty:
|
||||
// Data: 1531 bytes (59.8% Full)
|
||||
// But it would be nice to fix that in the future - might be hard to push the compiler to such a construct
|
||||
// @@TODO
|
||||
// The "ret" instruction actually is a serious overhead since it is present in every lambda function.
|
||||
// The only way around is a giant C-style switch which screws up the beauty of this array of registers.
|
||||
// But it will save a few bytes.
|
||||
static const RegisterRec registers[] PROGMEM = {
|
||||
// 0x00
|
||||
RegisterRec(false, &project_major),
|
||||
RegisterRec([]() -> uint16_t { return project_major; }),
|
||||
// 0x01
|
||||
RegisterRec(false, &project_minor),
|
||||
RegisterRec([]() -> uint16_t { return project_minor; }),
|
||||
// 0x02
|
||||
RegisterRec(false, &project_revision),
|
||||
RegisterRec([]() -> uint16_t { return project_revision; }),
|
||||
// 0x03
|
||||
RegisterRec(false, &project_build_number),
|
||||
RegisterRec([]() -> uint16_t { return project_build_number; }),
|
||||
// 0x04
|
||||
RegisterRec( // MMU errors
|
||||
[]() -> uint16_t { return mg::globals.DriveErrors(); }, // compiles to: <{lambda()#1}::_FUN()>: jmp <modules::permanent_storage::DriveError::get()>
|
||||
[]() -> uint16_t { return mg::globals.DriveErrors(); } // compiles to: <{lambda()#1}::_FUN()>: jmp <modules::permanent_storage::DriveError::get()>
|
||||
// [](uint16_t) {}, // @@TODO think about setting/clearing the error counter from the outside
|
||||
2),
|
||||
),
|
||||
// 0x05
|
||||
RegisterRec([]() -> uint16_t { return application.CurrentProgressCode(); }, 1),
|
||||
RegisterRec([]() -> uint16_t { return application.CurrentProgressCode(); }),
|
||||
// 0x06
|
||||
RegisterRec([]() -> uint16_t { return application.CurrentErrorCode(); }, 2),
|
||||
RegisterRec([]() -> uint16_t { return application.CurrentErrorCode(); }),
|
||||
// 0x07 filamentState
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.FilamentLoaded(); },
|
||||
[](uint16_t v) { return mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), static_cast<mg::FilamentLoadState>(v)); },
|
||||
1),
|
||||
[](uint16_t v) { return mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), static_cast<mg::FilamentLoadState>(v)); }),
|
||||
// 0x08 FINDA
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return static_cast<uint16_t>(mf::finda.Pressed()); },
|
||||
1),
|
||||
[]() -> uint16_t { return static_cast<uint16_t>(mf::finda.Pressed()); }),
|
||||
// 09 fsensor
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return static_cast<uint16_t>(mfs::fsensor.Pressed()); },
|
||||
[](uint16_t v) { return mfs::fsensor.ProcessMessage(v != 0); },
|
||||
1),
|
||||
[](uint16_t v) { return mfs::fsensor.ProcessMessage(v != 0); }),
|
||||
// 0xa motor mode (stealth = 1/normal = 0)
|
||||
RegisterRec([]() -> uint16_t { return static_cast<uint16_t>(mg::globals.MotorsStealth()); }, 1),
|
||||
RegisterRec([]() -> uint16_t { return static_cast<uint16_t>(mg::globals.MotorsStealth()); }),
|
||||
// 0xb extra load distance after fsensor triggered (30mm default) [mm] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.FSensorToNozzle_mm().v; },
|
||||
[](uint16_t d) { mg::globals.SetFSensorToNozzle_mm(d); },
|
||||
1),
|
||||
[](uint16_t d) { mg::globals.SetFSensorToNozzle_mm(d); }),
|
||||
// 0x0c fsensor unload check distance (40mm default) [mm] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.FSensorUnloadCheck_mm().v; },
|
||||
[](uint16_t d) { mg::globals.SetFSensorUnloadCheck_mm(d); },
|
||||
1),
|
||||
[](uint16_t d) { mg::globals.SetFSensorUnloadCheck_mm(d); }),
|
||||
|
||||
// 0xd 2 Pulley unload feedrate [mm/s] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.PulleyUnloadFeedrate_mm_s().v; },
|
||||
[](uint16_t d) { mg::globals.SetPulleyUnloadFeedrate_mm_s(d); },
|
||||
2),
|
||||
[](uint16_t d) { mg::globals.SetPulleyUnloadFeedrate_mm_s(d); }),
|
||||
|
||||
// 0xe Pulley acceleration [mm/s2] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return config::pulleyLimits.accel.v; },
|
||||
//@@TODO please update documentation as well
|
||||
2),
|
||||
[]() -> uint16_t {
|
||||
mm::steps_t val = mm::motion.Acceleration(config::Pulley);
|
||||
return mm::axisUnitToTruncatedUnit<config::U_mm_s2>(mm::P_accel_t({ val }));
|
||||
},
|
||||
[](uint16_t d) { mm::motion.SetAcceleration(config::Pulley, mm::unitToSteps<mm::P_accel_t>(config::U_mm_s2({ (long double)d }))); }),
|
||||
// 0xf Selector acceleration [mm/s2] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return config::selectorLimits.accel.v; },
|
||||
//@@TODO please update documentation as well
|
||||
2),
|
||||
[]() -> uint16_t {
|
||||
mm::steps_t val = mm::motion.Acceleration(config::Selector);
|
||||
return mm::axisUnitToTruncatedUnit<config::U_mm_s2>(mm::S_accel_t({ val }));
|
||||
},
|
||||
[](uint16_t d) { (mm::motion.SetAcceleration(config::Selector, mm::unitToSteps<mm::S_accel_t>(config::U_mm_s2({ (long double)d })))); }),
|
||||
// 0x10 Idler acceleration [deg/s2] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return config::idlerLimits.accel.v; },
|
||||
//@@TODO please update documentation as well
|
||||
2),
|
||||
[]() -> uint16_t {
|
||||
mm::steps_t val = mm::motion.Acceleration(config::Idler);
|
||||
return mm::axisUnitToTruncatedUnit<config::U_deg_s2>(mm::I_accel_t({ val }));
|
||||
},
|
||||
[](uint16_t d) { mm::motion.SetAcceleration(config::Idler, mm::unitToSteps<mm::I_accel_t>(config::U_deg_s2({ (long double)d }))); }),
|
||||
|
||||
// 0x11 Pulley load feedrate [mm/s] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.PulleyLoadFeedrate_mm_s().v; },
|
||||
[](uint16_t d) { mg::globals.SetPulleyLoadFeedrate_mm_s(d); },
|
||||
2),
|
||||
[](uint16_t d) { mg::globals.SetPulleyLoadFeedrate_mm_s(d); }),
|
||||
// 0x12 Selector nominal feedrate [mm/s] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.SelectorFeedrate_mm_s().v; },
|
||||
[](uint16_t d) { mg::globals.SetSelectorFeedrate_mm_s(d); },
|
||||
2),
|
||||
[](uint16_t d) { mg::globals.SetSelectorFeedrate_mm_s(d); }),
|
||||
// 0x13 Idler nominal feedrate [deg/s] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.IdlerFeedrate_deg_s().v; },
|
||||
[](uint16_t d) { mg::globals.SetIdlerFeedrate_deg_s(d); },
|
||||
2),
|
||||
[](uint16_t d) { mg::globals.SetIdlerFeedrate_deg_s(d); }),
|
||||
|
||||
// 0x14 Pulley slow load to fsensor feedrate [mm/s] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.PulleySlowFeedrate_mm_s().v; },
|
||||
[](uint16_t d) { mg::globals.SetPulleySlowFeedrate_mm_s(d); },
|
||||
2),
|
||||
[](uint16_t d) { mg::globals.SetPulleySlowFeedrate_mm_s(d); }),
|
||||
// 0x15 Selector homing feedrate [mm/s] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.SelectorHomingFeedrate_mm_s().v; },
|
||||
[](uint16_t d) { mg::globals.SetSelectorHomingFeedrate_mm_s(d); },
|
||||
2),
|
||||
[](uint16_t d) { mg::globals.SetSelectorHomingFeedrate_mm_s(d); }),
|
||||
// 0x16 Idler homing feedrate [deg/s] RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.IdlerHomingFeedrate_deg_s().v; },
|
||||
[](uint16_t d) { mg::globals.SetIdlerHomingFeedrate_deg_s(d); },
|
||||
2),
|
||||
[](uint16_t d) { mg::globals.SetIdlerHomingFeedrate_deg_s(d); }),
|
||||
|
||||
// 0x17 Pulley sg_thrs threshold RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.StallGuardThreshold(config::Pulley); },
|
||||
[](uint16_t d) { mg::globals.SetStallGuardThreshold(config::Pulley, d); },
|
||||
1),
|
||||
[](uint16_t d) { mg::globals.SetStallGuardThreshold(config::Pulley, d); }),
|
||||
// 0x18 Selector sg_thrs RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.StallGuardThreshold(mm::Axis::Selector); },
|
||||
[](uint16_t d) { mg::globals.SetStallGuardThreshold(mm::Axis::Selector, d); },
|
||||
1),
|
||||
[](uint16_t d) { mg::globals.SetStallGuardThreshold(mm::Axis::Selector, d); }),
|
||||
// 0x19 Idler sg_thrs RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.StallGuardThreshold(mm::Axis::Idler); },
|
||||
[](uint16_t d) { mg::globals.SetStallGuardThreshold(mm::Axis::Idler, d); },
|
||||
1),
|
||||
[](uint16_t d) { mg::globals.SetStallGuardThreshold(mm::Axis::Idler, d); }),
|
||||
|
||||
// 0x1a Get Pulley position [mm] R
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mpu::pulley.CurrentPosition_mm(); },
|
||||
2),
|
||||
[]() -> uint16_t { return mpu::pulley.CurrentPosition_mm(); }),
|
||||
// 0x1b Set/Get Selector slot RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return ms::selector.Slot(); },
|
||||
[](uint16_t d) { ms::selector.MoveToSlot(d); },
|
||||
1),
|
||||
[](uint16_t d) { ms::selector.MoveToSlot(d); }),
|
||||
// 0x1c Set/Get Idler slot RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mi::idler.Slot(); },
|
||||
[](uint16_t d) { d >= config::toolCount ? mi::idler.Disengage() : mi::idler.Engage(d); },
|
||||
1),
|
||||
[](uint16_t d) { d >= config::toolCount ? mi::idler.Disengage() : mi::idler.Engage(d); }),
|
||||
// 0x1d Set/Get Selector cut iRun current level RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.CutIRunCurrent(); },
|
||||
[](uint16_t d) { mg::globals.SetCutIRunCurrent(d); },
|
||||
1),
|
||||
[](uint16_t d) { mg::globals.SetCutIRunCurrent(d); }),
|
||||
|
||||
// 0x1e Get/Set Pulley iRun current RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mm::motion.CurrentsForAxis(config::Pulley).iRun; },
|
||||
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Pulley, d); },
|
||||
1),
|
||||
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Pulley, d); }),
|
||||
// 0x1f Set/Get Selector iRun current RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mm::motion.CurrentsForAxis(config::Selector).iRun; },
|
||||
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Selector, d); },
|
||||
1),
|
||||
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Selector, d); }),
|
||||
// 0x20 Set/Get Idler iRun current RW
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mm::motion.CurrentsForAxis(config::Idler).iRun; },
|
||||
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Idler, d); },
|
||||
1),
|
||||
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Idler, d); }),
|
||||
// 0x21 Current VCC voltage level R
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return 225; /*mv::vcc.CurrentBandgapVoltage();*/ },
|
||||
2),
|
||||
[]() -> uint16_t { return 225; /*mv::vcc.CurrentBandgapVoltage();*/ }),
|
||||
|
||||
// 0x22 Detected bowden length R
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mps::BowdenLength::Get(); },
|
||||
[](uint16_t d) { mps::BowdenLength::Set(d); },
|
||||
2),
|
||||
[](uint16_t d) { mps::BowdenLength::Set(d); }),
|
||||
|
||||
// 0x23 Cut length
|
||||
RegisterRec(
|
||||
[]() -> uint16_t { return mg::globals.CutLength().v; },
|
||||
[](uint16_t d) { mg::globals.SetCutLength(d); },
|
||||
2),
|
||||
[](uint16_t d) { mg::globals.SetCutLength(d); }),
|
||||
};
|
||||
|
||||
static constexpr uint8_t registersSize = sizeof(registers) / sizeof(RegisterRec);
|
||||
|
|
@ -457,59 +392,27 @@ bool ReadRegister(uint8_t address, uint16_t &value) {
|
|||
|
||||
// Get pointer to register at address
|
||||
const uint8_t *addr = reinterpret_cast<const uint8_t *>(registers + address);
|
||||
const RegisterFlags rf(hal::progmem::read_byte(addr));
|
||||
|
||||
// beware - abusing the knowledge of RegisterRec memory layout to do lpm_reads
|
||||
const void *varAddr = addr + sizeof(RegisterFlags);
|
||||
if (!rf.RWFuncs()) {
|
||||
switch (rf.Size()) {
|
||||
case 0:
|
||||
case 1:
|
||||
value = *hal::progmem::read_ptr<const uint8_t *>(varAddr);
|
||||
break;
|
||||
case 2:
|
||||
value = *hal::progmem::read_ptr<const uint16_t *>(varAddr);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
auto readFunc = hal::progmem::read_ptr<const TReadFunc>(varAddr);
|
||||
value = readFunc();
|
||||
return true;
|
||||
}
|
||||
const void *varAddr = addr;
|
||||
auto readFunc = hal::progmem::read_ptr<const TReadFunc>(varAddr);
|
||||
value = readFunc();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteRegister(uint8_t address, uint16_t value) {
|
||||
if (address >= registersSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t *addr = reinterpret_cast<const uint8_t *>(registers + address);
|
||||
const RegisterFlags rf(hal::progmem::read_byte(addr));
|
||||
|
||||
if (!rf.Writable()) {
|
||||
// beware - abusing the knowledge of RegisterRec memory layout to do lpm_reads
|
||||
// addr offset should be 2 on AVR, but 8 on x86_64, therefore "sizeof(void*)"
|
||||
const void *varAddr = addr + sizeof(RegisterRec::A1);
|
||||
auto writeFunc = hal::progmem::read_ptr<const TWriteFunc>(varAddr);
|
||||
if (writeFunc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// beware - abusing the knowledge of RegisterRec memory layout to do lpm_reads
|
||||
// addr offset should be 3 on AVR, but 9 on x86_64, therefore "1 + sizeof(void*)"
|
||||
const void *varAddr = addr + sizeof(RegisterFlags) + sizeof(RegisterRec::A1);
|
||||
if (!rf.RWFuncs()) {
|
||||
switch (rf.Size()) {
|
||||
case 0:
|
||||
case 1:
|
||||
*hal::progmem::read_ptr<uint8_t *>(varAddr) = value;
|
||||
break;
|
||||
case 2:
|
||||
*hal::progmem::read_ptr<uint16_t *>(varAddr) = value;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
auto writeFunc = hal::progmem::read_ptr<const TWriteFunc>(varAddr);
|
||||
writeFunc(value);
|
||||
return true;
|
||||
}
|
||||
writeFunc(value);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ void FailingMovableUnload(hal::tmc2130::ErrorFlags ef, ErrorCode ec, config::Axi
|
|||
// UnloadFilament starts by engaging the idler (through the UnloadToFinda state machine)
|
||||
uf.Reset(0);
|
||||
|
||||
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), 0, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
|
||||
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), 0, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
|
||||
|
||||
REQUIRE(WhileCondition(
|
||||
uf,
|
||||
|
|
|
|||
|
|
@ -84,6 +84,13 @@ void LoadFilamentSuccessful(uint8_t slot, logic::LoadFilament &lf) {
|
|||
void LoadFilamentSuccessfulWithRehomeSelector(uint8_t slot, logic::LoadFilament &lf) {
|
||||
// Stage 2 - feeding to finda
|
||||
// make FINDA switch on
|
||||
// engaging idler
|
||||
|
||||
REQUIRE(WhileCondition(
|
||||
lf,
|
||||
[&](uint32_t) { return !mi::idler.Engaged(); },
|
||||
5000));
|
||||
|
||||
REQUIRE(WhileCondition(lf, std::bind(SimulateFeedToFINDA, _1, 100), 5000));
|
||||
REQUIRE(VerifyState(lf, mg::FilamentLoadState::InSelector, slot, slot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::RetractingFromFinda));
|
||||
|
||||
|
|
@ -176,8 +183,6 @@ void FailedLoadToFindaResolveTryAgain(uint8_t slot, logic::LoadFilament &lf) {
|
|||
REQUIRE(VerifyState(lf, mg::FilamentLoadState::InSelector, config::toolCount, slot, false, false, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::FeedingToFinda));
|
||||
ClearButtons(lf);
|
||||
|
||||
SimulateIdlerHoming(lf);
|
||||
|
||||
LoadFilamentSuccessfulWithRehomeSelector(slot, lf);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include "../stubs/stub_motion.h"
|
||||
|
||||
#include "catch2/catch_test_macros.hpp"
|
||||
|
||||
void SimulateIdlerAndSelectorHoming(logic::CommandBase &cb) {
|
||||
#if 0
|
||||
// do 5 steps until we trigger the simulated StallGuard
|
||||
|
|
@ -58,6 +60,18 @@ void SimulateIdlerAndSelectorHoming(logic::CommandBase &cb) {
|
|||
|
||||
void SimulateIdlerHoming(logic::CommandBase &cb) {
|
||||
uint32_t idlerStepsFwd = mm::unitToSteps<mm::I_pos_t>(config::idlerLimits.lenght - 5.0_deg);
|
||||
|
||||
// Sometimes the initial idler state is Ready. Let's wait for the firmware to start
|
||||
// homing.
|
||||
REQUIRE(WhileCondition(
|
||||
cb,
|
||||
[&](uint32_t) { return mi::idler.State() == mm::MovableBase::Ready; },
|
||||
5000));
|
||||
|
||||
// At this point the idler should always be homing forward.
|
||||
REQUIRE((int)mi::idler.State() == (int)mm::MovableBase::HomeForward);
|
||||
|
||||
// Simulate the idler steps in one direction (forward)
|
||||
for (uint32_t i = 0; i < idlerStepsFwd; ++i) {
|
||||
main_loop();
|
||||
cb.Step();
|
||||
|
|
@ -68,6 +82,8 @@ void SimulateIdlerHoming(logic::CommandBase &cb) {
|
|||
cb.Step();
|
||||
mm::motion.StallGuardReset(mm::Idler);
|
||||
|
||||
REQUIRE((int)mi::idler.State() == (int)mm::MovableBase::HomeBack);
|
||||
|
||||
// now do a correct amount of steps of each axis towards the other end
|
||||
uint32_t idlerSteps = mm::unitToSteps<mm::I_pos_t>(config::idlerLimits.lenght);
|
||||
uint32_t maxSteps = idlerSteps + 1;
|
||||
|
|
@ -82,6 +98,9 @@ void SimulateIdlerHoming(logic::CommandBase &cb) {
|
|||
mm::motion.StallGuardReset(mm::Idler);
|
||||
}
|
||||
}
|
||||
|
||||
// If the homing has failed, the axis length was too short.
|
||||
REQUIRE(!((mi::idler.State() & mm::MovableBase::HomingFailed) == mm::MovableBase::HomingFailed));
|
||||
}
|
||||
|
||||
void SimulateIdlerWaitForHomingValid(logic::CommandBase &cb) {
|
||||
|
|
@ -185,33 +204,40 @@ bool SimulateFailedHomeSelectorPostfix(logic::CommandBase &cb) {
|
|||
}
|
||||
|
||||
bool SimulateFailedHomeFirstTime(logic::CommandBase &cb) {
|
||||
if (mi::idler.HomingValid())
|
||||
return false;
|
||||
if (ms::selector.HomingValid())
|
||||
return false;
|
||||
REQUIRE(!mi::idler.HomingValid());
|
||||
REQUIRE(!ms::selector.HomingValid());
|
||||
|
||||
// Idler homing is successful
|
||||
SimulateIdlerHoming(cb);
|
||||
SimulateIdlerWaitForHomingValid(cb);
|
||||
|
||||
// Selector homes once the idler homing is valid.
|
||||
REQUIRE(mi::idler.HomingValid());
|
||||
REQUIRE(!ms::selector.HomingValid());
|
||||
|
||||
// The selector will only rehome once the idler homing is valid. At that moment
|
||||
// the state will change to HomeForward.
|
||||
REQUIRE(WhileCondition(
|
||||
cb,
|
||||
[&](uint32_t) { return ms::selector.State() != mm::MovableBase::HomeForward; },
|
||||
5000));
|
||||
|
||||
constexpr uint32_t selectorSteps = mm::unitToSteps<mm::S_pos_t>(config::selectorLimits.lenght) + 1;
|
||||
{
|
||||
// do 5 steps until we trigger the simulated StallGuard
|
||||
constexpr uint32_t idlerStepsFwd = mm::unitToSteps<mm::I_pos_t>(config::idlerLimits.lenght - 5.0_deg);
|
||||
static_assert(idlerStepsFwd < selectorSteps); // beware, we expect that the Idler homes faster than Selector (less steps)
|
||||
for (uint32_t i = 0; i < idlerStepsFwd; ++i) {
|
||||
for (uint32_t i = 0; i < selectorSteps; ++i) {
|
||||
main_loop();
|
||||
cb.Step();
|
||||
}
|
||||
|
||||
mm::TriggerStallGuard(mm::Selector);
|
||||
mm::TriggerStallGuard(mm::Idler);
|
||||
main_loop();
|
||||
cb.Step();
|
||||
mm::motion.StallGuardReset(mm::Selector);
|
||||
mm::motion.StallGuardReset(mm::Idler);
|
||||
}
|
||||
// now do a correct amount of steps of each axis towards the other end
|
||||
constexpr uint32_t idlerSteps = mm::unitToSteps<mm::I_pos_t>(config::idlerLimits.lenght);
|
||||
// now do LESS steps than expected to simulate something is blocking the selector
|
||||
|
||||
constexpr uint32_t selectorTriggerShort = std::min(idlerSteps, selectorSteps) / 2;
|
||||
// now do LESS steps than expected to simulate something is blocking the selector
|
||||
constexpr uint32_t selectorTriggerShort = selectorSteps / 2;
|
||||
constexpr uint32_t maxSteps = selectorTriggerShort + 1;
|
||||
{
|
||||
for (uint32_t i = 0; i < maxSteps; ++i) {
|
||||
|
|
@ -225,17 +251,6 @@ bool SimulateFailedHomeFirstTime(logic::CommandBase &cb) {
|
|||
}
|
||||
}
|
||||
|
||||
// make sure the Idler finishes its homing procedure (makes further checks much easier)
|
||||
for (uint32_t i = maxSteps; i < idlerSteps + 1; ++i) {
|
||||
main_loop();
|
||||
cb.Step();
|
||||
if (i == idlerSteps) {
|
||||
mm::TriggerStallGuard(mm::Idler);
|
||||
} else {
|
||||
mm::motion.StallGuardReset(mm::Idler);
|
||||
}
|
||||
}
|
||||
|
||||
while (!(ms::selector.State() & mm::MovableBase::OnHold)) {
|
||||
main_loop();
|
||||
cb.Step();
|
||||
|
|
|
|||
|
|
@ -79,19 +79,27 @@ void ForceReinitAllAutomata() {
|
|||
mg::globals.Init();
|
||||
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::AtPulley);
|
||||
}
|
||||
void HomeIdler() {
|
||||
logic::NoCommand nc; // just a dummy instance which has an empty Step()
|
||||
SimulateIdlerHoming(nc);
|
||||
SimulateIdlerWaitForHomingValid(nc);
|
||||
SimulateIdlerMoveToParkingPosition(nc);
|
||||
}
|
||||
|
||||
void HomeSelector() {
|
||||
logic::NoCommand nc; // just a dummy instance which has an empty Step()
|
||||
SimulateSelectorHoming(nc);
|
||||
SimulateSelectorWaitForHomingValid(nc);
|
||||
SimulateSelectorWaitForReadyState(nc);
|
||||
}
|
||||
|
||||
void HomeIdlerAndSelector() {
|
||||
mi::idler.InvalidateHoming();
|
||||
ms::selector.InvalidateHoming();
|
||||
logic::NoCommand nc; // just a dummy instance which has an empty Step()
|
||||
|
||||
SimulateIdlerHoming(nc);
|
||||
SimulateIdlerWaitForHomingValid(nc);
|
||||
SimulateIdlerMoveToParkingPosition(nc);
|
||||
HomeIdler();
|
||||
|
||||
SimulateSelectorHoming(nc);
|
||||
SimulateSelectorWaitForHomingValid(nc);
|
||||
SimulateSelectorWaitForReadyState(nc);
|
||||
HomeSelector();
|
||||
}
|
||||
|
||||
bool EnsureActiveSlotIndex(uint8_t slot, mg::FilamentLoadState loadState) {
|
||||
|
|
@ -181,10 +189,10 @@ void SimulateErrDisengagingIdler(logic::CommandBase &cb, ErrorCode deferredEC) {
|
|||
REQUIRE(WhileCondition(
|
||||
cb, [&](uint32_t) {
|
||||
if (cb.TopLevelState() == ProgressCode::ERRDisengagingIdler) {
|
||||
REQUIRE(cb.Error() == ErrorCode::RUNNING); // ensure the error gets never set while disengaging the idler
|
||||
REQUIRE((cb.Error() == ErrorCode::RUNNING)); // ensure the error gets never set while disengaging the idler
|
||||
return true;
|
||||
} else {
|
||||
REQUIRE(cb.Error() == deferredEC);
|
||||
REQUIRE((cb.Error() == deferredEC));
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include "../../../../src/logic/command_base.h"
|
||||
#include "../../../../src/modules/globals.h"
|
||||
#include "../../../../src/modules/idler.h"
|
||||
|
||||
extern void main_loop();
|
||||
extern void ForceReinitAllAutomata();
|
||||
|
|
@ -44,3 +45,21 @@ static constexpr uint32_t selectorMoveMaxSteps = 40000UL;
|
|||
void HomeIdlerAndSelector();
|
||||
|
||||
void SimulateErrDisengagingIdler(logic::CommandBase &cb, ErrorCode deferredEC);
|
||||
|
||||
template <typename T>
|
||||
bool SimulateEngageIdlerFully(T &cb) {
|
||||
return WhileCondition(
|
||||
cb,
|
||||
[&](uint32_t) { return !mi::idler.Engaged(); },
|
||||
5000);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool SimulateEngageIdlerPartially(T &cb) {
|
||||
return WhileCondition(
|
||||
cb,
|
||||
[&](uint32_t) { return !mi::idler.PartiallyDisengaged(); },
|
||||
5000);
|
||||
}
|
||||
|
||||
void HomeIdler();
|
||||
|
|
|
|||
|
|
@ -328,13 +328,43 @@ void ToolChangeFailFSensor(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSl
|
|||
// restart the automaton
|
||||
tc.Reset(toSlot);
|
||||
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InNozzle, mi::idler.IdleSlotIndex(), fromSlot, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
|
||||
// simulate unload to finda but fail the fsensor test
|
||||
REQUIRE(WhileCondition(tc, std::bind(SimulateUnloadToFINDA, _1, 500'000, 10'000), 200'000));
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), fromSlot, false, false, ml::off, ml::blink0, ErrorCode::FSENSOR_DIDNT_SWITCH_OFF, ProgressCode::UnloadingFilament));
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InNozzle, mi::idler.IdleSlotIndex(), fromSlot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
|
||||
|
||||
// now simulate unload to finda but fail the fsensor test - that means basically waiting for 4 seconds
|
||||
for (uint32_t ms = 0; ms < 4000; ++ms) {
|
||||
main_loop(); // increments 1ms each run
|
||||
tc.Step();
|
||||
}
|
||||
|
||||
// UnloadFilament.unl finds out, that fsensor didn't turn off in time
|
||||
main_loop();
|
||||
tc.Step();
|
||||
|
||||
// UnloadFilament starts error handling
|
||||
main_loop();
|
||||
tc.Step();
|
||||
|
||||
// wait for the Idler to be disengaged in error state
|
||||
REQUIRE(WhileCondition(
|
||||
tc, [&](uint32_t) {
|
||||
return tc.State() == ProgressCode::ERRDisengagingIdler;
|
||||
},
|
||||
idlerEngageDisengageMaxSteps));
|
||||
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InNozzle, mi::idler.IdleSlotIndex(), fromSlot, true, false, ml::off, ml::blink0, ErrorCode::FSENSOR_DIDNT_SWITCH_OFF, ProgressCode::UnloadingFilament));
|
||||
REQUIRE(tc.unl.State() == ProgressCode::ERRWaitingForUser);
|
||||
}
|
||||
|
||||
bool SimulateUnloadFilamentUntilSelectorRehoming(uint32_t step, const logic::ToolChange *tc, uint32_t unloadLengthSteps) {
|
||||
if (step == 20) { // on 20th step make FSensor switch off
|
||||
mfs::fsensor.ProcessMessage(false);
|
||||
} else if (step == unloadLengthSteps) {
|
||||
FINDAOnOff(false);
|
||||
}
|
||||
// end simulation at the DisengagingIdler stage, selector re-homing will take place later
|
||||
return tc->unl.State() != ProgressCode::DisengagingIdler;
|
||||
}
|
||||
|
||||
void ToolChangeFailFSensorMiddleBtn(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSlot) {
|
||||
using namespace std::placeholders;
|
||||
|
||||
|
|
@ -343,43 +373,31 @@ void ToolChangeFailFSensorMiddleBtn(logic::ToolChange &tc, uint8_t fromSlot, uin
|
|||
REQUIRE_FALSE(mui::userInput.AnyEvent());
|
||||
PressButtonAndDebounce(tc, mb::Middle, true);
|
||||
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), fromSlot, false, false, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
|
||||
REQUIRE(tc.unl.State() == ProgressCode::FeedingToFinda); // MMU must find out where the filament is FS is OFF, FINDA is OFF
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InNozzle, mi::idler.IdleSlotIndex(), fromSlot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
|
||||
|
||||
// both movables should have their homing flag invalidated
|
||||
REQUIRE_FALSE(mi::idler.HomingValid());
|
||||
REQUIRE_FALSE(ms::selector.HomingValid());
|
||||
|
||||
// make FINDA trigger - Idler will rehome in this step, Selector must remain at its place
|
||||
SimulateIdlerHoming(tc);
|
||||
|
||||
REQUIRE_FALSE(mi::idler.HomingValid());
|
||||
REQUIRE_FALSE(ms::selector.HomingValid());
|
||||
|
||||
SimulateIdlerWaitForHomingValid(tc);
|
||||
|
||||
REQUIRE(mi::idler.HomingValid());
|
||||
REQUIRE_FALSE(ms::selector.HomingValid());
|
||||
|
||||
SimulateIdlerMoveToParkingPosition(tc);
|
||||
|
||||
// now trigger the FINDA
|
||||
REQUIRE(WhileCondition(tc, std::bind(SimulateFeedToFINDA, _1, 100), 5000));
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, fromSlot, fromSlot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
|
||||
REQUIRE(tc.unl.State() == ProgressCode::RetractingFromFinda);
|
||||
|
||||
// make FINDA switch off
|
||||
REQUIRE(WhileCondition(tc, std::bind(SimulateRetractFromFINDA, _1, 100), 5000));
|
||||
// perform a successful unload
|
||||
// During the last stage (DisengagingIdler), the Selector will start re-homing - needs special handling
|
||||
REQUIRE(WhileCondition(
|
||||
tc, [&](uint32_t) { return tc.unl.State() == ProgressCode::RetractingFromFinda; }, 50000));
|
||||
tc,
|
||||
std::bind(SimulateUnloadFilamentUntilSelectorRehoming, _1, &tc, mm::unitToSteps<mm::P_pos_t>(config::minimumBowdenLength)),
|
||||
200'000UL));
|
||||
|
||||
// Selector will start rehoming at this stage - that was the error this test was to find
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::AtPulley, fromSlot, config::toolCount, false, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
|
||||
REQUIRE(tc.unl.State() == ProgressCode::DisengagingIdler);
|
||||
SimulateSelectorHoming(tc);
|
||||
SimulateSelectorWaitForHomingValid(tc);
|
||||
|
||||
// Selector should be moving to the target slot to accomplish the unload phase
|
||||
REQUIRE(WhileTopState(tc, ProgressCode::UnloadingFilament, 50'000));
|
||||
|
||||
// Idler has probably engaged meanwhile, ignore its position check
|
||||
REQUIRE(WhileTopState(tc, ProgressCode::UnloadingFilament, 50000));
|
||||
REQUIRE(VerifyState2(tc, mg::FilamentLoadState::AtPulley, config::toolCount, fromSlot, false, false, toSlot, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::FeedingToFinda));
|
||||
|
||||
// after that, perform a normal load
|
||||
|
|
@ -528,7 +546,7 @@ void ToolChangeFSENSOR_TOO_EARLY(logic::ToolChange &tc, uint8_t slot) {
|
|||
|
||||
// make AutoRetry
|
||||
PressButtonAndDebounce(tc, mb::Middle, true);
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), slot, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
|
||||
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), slot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
|
||||
|
||||
SimulateIdlerHoming(tc);
|
||||
|
||||
|
|
|
|||
|
|
@ -53,16 +53,10 @@ void RegularUnloadFromSlot04(uint8_t slot, logic::UnloadFilament &uf, uint8_t en
|
|||
REQUIRE(VerifyState(uf, (mg::FilamentLoadState)(mg::FilamentLoadState::InNozzle | mg::FilamentLoadState::InSelector),
|
||||
entryIdlerSlotIndex, slot, true, true, entryGreenLED, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
|
||||
|
||||
// run the automaton
|
||||
// Stage 1 - unloading to FINDA
|
||||
REQUIRE(WhileCondition(
|
||||
uf,
|
||||
[&](uint32_t step) -> bool {
|
||||
if(step == 100){ // on 100th step make FINDA trigger
|
||||
hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::low);
|
||||
}
|
||||
return uf.TopLevelState() == ProgressCode::UnloadingToFinda; },
|
||||
50000));
|
||||
REQUIRE(WhileCondition(uf, std::bind(SimulateUnloadToFINDA, _1, 100, 2'000), 200'000));
|
||||
|
||||
main_loop();
|
||||
uf.Step();
|
||||
|
||||
// we still think we have filament loaded at this stage
|
||||
// idler should have been activated by the underlying automaton
|
||||
|
|
@ -100,7 +94,7 @@ TEST_CASE("unload_filament::regular_unload_from_slot_0-4", "[unload_filament]")
|
|||
for (uint8_t slot = 0; slot < config::toolCount; ++slot) {
|
||||
logic::UnloadFilament uf;
|
||||
RegularUnloadFromSlot04Init(slot, uf);
|
||||
RegularUnloadFromSlot04(slot, uf, mi::Idler::IdleSlotIndex(), false, ml::off);
|
||||
RegularUnloadFromSlot04(slot, uf, mi::Idler::IdleSlotIndex(), false, ml::blink0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +124,7 @@ void FindaDidntTriggerCommonSetup(uint8_t slot, logic::UnloadFilament &uf) {
|
|||
// FINDA triggered off
|
||||
// green LED should be off
|
||||
// no error so far
|
||||
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), slot, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
|
||||
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), slot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
|
||||
|
||||
// run the automaton
|
||||
// Stage 1 - unloading to FINDA - do NOT let it trigger - keep it pressed, the automaton should finish all moves with the pulley
|
||||
|
|
@ -177,7 +171,7 @@ void FindaDidntTriggerResolveTryAgain(uint8_t slot, logic::UnloadFilament &uf) {
|
|||
// no change in selector's position
|
||||
// FINDA still on
|
||||
// red LED should blink, green LED should be off
|
||||
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InSelector, mi::Idler::IdleSlotIndex(), slot, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
|
||||
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InSelector, mi::Idler::IdleSlotIndex(), slot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
|
||||
|
||||
ClearButtons(uf);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,12 @@
|
|||
#include "../../../../src/modules/motion.h"
|
||||
#include "../../../../src/modules/permanent_storage.h"
|
||||
#include "../../../../src/modules/selector.h"
|
||||
#include "../../../../src/modules/timebase.h"
|
||||
|
||||
#include "../../../../src/logic/unload_to_finda.h"
|
||||
|
||||
#include "../../modules/stubs/stub_adc.h"
|
||||
#include "../../modules/stubs/stub_timebase.h"
|
||||
|
||||
#include "../stubs/main_loop_stub.h"
|
||||
#include "../stubs/stub_motion.h"
|
||||
|
|
@ -24,7 +26,7 @@ using namespace std::placeholders;
|
|||
|
||||
namespace ha = hal::adc;
|
||||
|
||||
TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
|
||||
void UnloadToFindaCommonSetup(logic::UnloadToFinda &ff, uint8_t retryAttempts) {
|
||||
ForceReinitAllAutomata();
|
||||
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
|
||||
|
||||
|
|
@ -35,27 +37,41 @@ TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
|
|||
// and MMU "thinks" it has the filament loaded
|
||||
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
|
||||
|
||||
logic::UnloadToFinda ff;
|
||||
|
||||
// restart the automaton - just 1 attempt
|
||||
ff.Reset(1);
|
||||
ff.Reset(retryAttempts);
|
||||
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
|
||||
|
||||
// it should have instructed the selector and idler to move to slot 1
|
||||
// check if the idler and selector have the right command
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::IntermediateSlotPosition(0).v);
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
|
||||
|
||||
// engaging idler
|
||||
REQUIRE(WhileCondition(
|
||||
ff,
|
||||
[&](uint32_t) { return !mi::idler.Engaged(); },
|
||||
5000));
|
||||
REQUIRE(SimulateEngageIdlerPartially(ff));
|
||||
}
|
||||
|
||||
void UnloadToFindaCommonTurnOffFSensor(logic::UnloadToFinda &ff) {
|
||||
// turn off fsensor - the printer freed the filament from the gears
|
||||
SetFSensorStateAndDebounce(false);
|
||||
|
||||
// make sure we step ff to handle turned-off fsensor
|
||||
ff.Step();
|
||||
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::UnloadingToFinda);
|
||||
CHECK(mm::axes[mm::Pulley].enabled == true);
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
|
||||
|
||||
REQUIRE(SimulateEngageIdlerFully(ff));
|
||||
|
||||
// now pulling the filament until finda triggers
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
|
||||
}
|
||||
|
||||
TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
|
||||
logic::UnloadToFinda ff;
|
||||
UnloadToFindaCommonSetup(ff, 1);
|
||||
UnloadToFindaCommonTurnOffFSensor(ff);
|
||||
|
||||
REQUIRE(WhileCondition(ff, std::bind(SimulateUnloadToFINDA, _1, 10, 1000), 1100));
|
||||
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::OK);
|
||||
|
|
@ -77,37 +93,9 @@ TEST_CASE("unload_to_finda::no_sense_FINDA_upon_start", "[unload_to_finda]") {
|
|||
}
|
||||
|
||||
TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]") {
|
||||
ForceReinitAllAutomata();
|
||||
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
|
||||
|
||||
// we need finda ON
|
||||
SetFINDAStateAndDebounce(true);
|
||||
// fsensor should be ON
|
||||
SetFSensorStateAndDebounce(true);
|
||||
// and MMU "thinks" it has the filament loaded
|
||||
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
|
||||
|
||||
logic::UnloadToFinda ff;
|
||||
|
||||
// restart the automaton - just 1 attempt
|
||||
ff.Reset(1);
|
||||
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
|
||||
|
||||
// it should have instructed the selector and idler to move to slot 1
|
||||
// check if the idler and selector have the right command
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
|
||||
CHECK(mm::axes[mm::Idler].enabled == true);
|
||||
|
||||
// engaging idler
|
||||
REQUIRE(WhileCondition(
|
||||
ff,
|
||||
[&](uint32_t) { return !mi::idler.Engaged(); },
|
||||
5000));
|
||||
|
||||
// now pulling the filament until finda triggers
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
|
||||
UnloadToFindaCommonSetup(ff, 1);
|
||||
UnloadToFindaCommonTurnOffFSensor(ff);
|
||||
|
||||
// no changes to FINDA during unload - we'll pretend it never triggers
|
||||
// but set FSensor correctly
|
||||
|
|
@ -119,96 +107,67 @@ TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]")
|
|||
}
|
||||
|
||||
TEST_CASE("unload_to_finda::unload_without_FSensor_trigger", "[unload_to_finda]") {
|
||||
ForceReinitAllAutomata();
|
||||
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
|
||||
|
||||
// we need finda ON
|
||||
SetFINDAStateAndDebounce(true);
|
||||
// fsensor should be ON
|
||||
SetFSensorStateAndDebounce(true);
|
||||
// and MMU "thinks" it has the filament loaded
|
||||
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
|
||||
|
||||
logic::UnloadToFinda ff;
|
||||
|
||||
// restart the automaton - just 1 attempt
|
||||
ff.Reset(1);
|
||||
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
|
||||
|
||||
// it should have instructed the selector and idler to move to slot 1
|
||||
// check if the idler and selector have the right command
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
|
||||
CHECK(mm::axes[mm::Idler].enabled == true);
|
||||
|
||||
// engaging idler
|
||||
REQUIRE(WhileCondition(
|
||||
ff,
|
||||
[&](uint32_t) { return !mi::idler.Engaged(); },
|
||||
5000));
|
||||
|
||||
// now pulling the filament until finda triggers
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
|
||||
UnloadToFindaCommonSetup(ff, 1);
|
||||
|
||||
// no changes to FSensor during unload - we'll pretend it never triggers
|
||||
// but set FINDA correctly
|
||||
REQUIRE(WhileCondition(ff, std::bind(SimulateUnloadToFINDA, _1, 150000, 10000), 50000));
|
||||
// time-out in 4 seconds
|
||||
mt::IncMillis(4000);
|
||||
|
||||
main_loop();
|
||||
ff.Step();
|
||||
|
||||
// no pulling actually starts, because the fsensor didn't turn off and the time-out elapsed
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::FailedFSensor);
|
||||
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InSelector);
|
||||
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InNozzle);
|
||||
}
|
||||
|
||||
TEST_CASE("unload_to_finda::unload_repeated", "[unload_to_finda]") {
|
||||
ForceReinitAllAutomata();
|
||||
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
|
||||
|
||||
// we need finda ON
|
||||
SetFINDAStateAndDebounce(true);
|
||||
// fsensor should be ON
|
||||
SetFSensorStateAndDebounce(true);
|
||||
// and MMU "thinks" it has the filament loaded
|
||||
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
|
||||
|
||||
logic::UnloadToFinda ff;
|
||||
UnloadToFindaCommonSetup(ff, 2);
|
||||
|
||||
// restart the automaton - 2 attempts
|
||||
ff.Reset(2);
|
||||
UnloadToFindaCommonTurnOffFSensor(ff);
|
||||
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
|
||||
|
||||
// it should have instructed the selector and idler to move to slot 1
|
||||
// check if the idler and selector have the right command
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
|
||||
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
|
||||
CHECK(mm::axes[mm::Idler].enabled == true);
|
||||
|
||||
// engaging idler
|
||||
REQUIRE(WhileCondition(
|
||||
ff,
|
||||
[&](uint32_t) { return !mi::idler.Engaged(); },
|
||||
5000));
|
||||
|
||||
// now pulling the filament until finda triggers
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
|
||||
// remember raw Pulley pos for tweaking the steps below
|
||||
// because a 20mm (config::fsensorToNozzleAvoidGrindUnload)
|
||||
// move is being executed while the Idler is fully engaging
|
||||
// It is roughly -90 steps
|
||||
// int32_t pulleySteppedAlready = mm::axes[config::Pulley].pos;
|
||||
|
||||
// no changes to FINDA during unload - we'll pretend it never triggers
|
||||
// but set FSensor correctly
|
||||
// In this case it is vital to correctly compute the amount of steps
|
||||
// to make the unload state machine restart after the 1st attempt
|
||||
uint32_t unlSteps = 1 + mm::unitToSteps<mm::P_pos_t>(config::maximumBowdenLength + config::feedToFinda + config::filamentMinLoadedToMMU);
|
||||
// The number of steps must be more than what the state machine expects for FINDA to trigger.
|
||||
uint32_t unlSteps = 1 + mm::unitToSteps<mm::P_pos_t>(
|
||||
// standard fast move distance
|
||||
config::maximumBowdenLength + config::feedToFinda + config::filamentMinLoadedToMMU
|
||||
// slow start move distance
|
||||
+ config::fsensorToNozzleAvoidGrindUnload);
|
||||
// compensation
|
||||
// + pulleySteppedAlready;
|
||||
REQUIRE_FALSE(WhileCondition(ff, std::bind(SimulateUnloadToFINDA, _1, 10, 150000), unlSteps));
|
||||
|
||||
main_loop();
|
||||
ff.Step();
|
||||
|
||||
REQUIRE_FALSE(mi::idler.HomingValid());
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
|
||||
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InSelector);
|
||||
|
||||
HomeIdler();
|
||||
|
||||
main_loop();
|
||||
ff.Step();
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::UnloadingToFinda);
|
||||
|
||||
SimulateEngageIdlerPartially(ff);
|
||||
|
||||
main_loop();
|
||||
ff.Step();
|
||||
|
||||
REQUIRE(ff.State() == logic::UnloadToFinda::UnloadingToFinda);
|
||||
SimulateEngageIdlerFully(ff);
|
||||
|
||||
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InSelector);
|
||||
|
||||
// make arbitrary amount of steps
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
# define the test executable
|
||||
add_executable(
|
||||
leds_tests ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${MODULES_STUBS_DIR}/stub_shr16.cpp
|
||||
${MODULES_STUBS_DIR}/stub_timebase.cpp test_leds.cpp
|
||||
leds_tests
|
||||
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
|
||||
${MODULES_STUBS_DIR}/stub_shr16.cpp
|
||||
${MODULES_STUBS_DIR}/stub_timebase.cpp
|
||||
${MODULES_STUBS_DIR}/stub_eeprom.cpp
|
||||
test_leds.cpp
|
||||
)
|
||||
|
||||
# define required search paths
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import tarfile
|
|||
import zipfile
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
from tarfile import TarFile
|
||||
from urllib.request import urlretrieve
|
||||
project_root_dir = Path(__file__).resolve().parent.parent
|
||||
dependencies_dir = project_root_dir / '.dependencies'
|
||||
|
|
@ -28,11 +29,11 @@ dependencies_dir = project_root_dir / '.dependencies'
|
|||
# yapf: disable
|
||||
dependencies = {
|
||||
'ninja': {
|
||||
'version': '1.10.2',
|
||||
'version': '1.12.1',
|
||||
'url': {
|
||||
'Linux': 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-linux.zip',
|
||||
'Windows': 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-win.zip',
|
||||
'Darwin': 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip',
|
||||
'Linux': 'https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux.zip',
|
||||
'Windows': 'https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-win.zip',
|
||||
'Darwin': 'https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-mac.zip',
|
||||
},
|
||||
},
|
||||
'cmake': {
|
||||
|
|
@ -99,7 +100,11 @@ def download_and_unzip(url: str, directory: Path):
|
|||
obj = tarfile.open(f)
|
||||
else:
|
||||
obj = zipfile.ZipFile(f, 'r')
|
||||
obj.extractall(path=str(extract_dir))
|
||||
|
||||
if isinstance(obj, TarFile):
|
||||
obj.extractall(path=str(extract_dir), filter='data')
|
||||
else: # Zip file
|
||||
obj.extractall(path=str(extract_dir))
|
||||
|
||||
subdir = find_single_subdir(extract_dir)
|
||||
shutil.move(str(subdir), str(directory))
|
||||
|
|
|
|||
|
|
@ -93,11 +93,11 @@ pipeline {
|
|||
steps {
|
||||
sh """
|
||||
python3 utils/bootstrap.py
|
||||
export PATH=\$PWD/.dependencies/cmake-3.22.5/bin:\$PWD/.dependencies/ninja-1.10.2:\$PATH
|
||||
export PATH=\$PWD/.dependencies/cmake-3.22.5/bin:\$PWD/.dependencies/ninja-1.12.1:\$PATH
|
||||
export CTEST_OUTPUT_ON_FAILURE=1
|
||||
mkdir -p build-test
|
||||
LD_LIBRARY_PATH=/usr/local/lib32 \$PWD/.dependencies/cmake-3.22.5/bin/ctest --build-and-test . build-test \
|
||||
-DCMAKE_MAKE_PROGRAM=\$PWD/.dependencies/ninja-1.10.2/ninja \
|
||||
-DCMAKE_MAKE_PROGRAM=\$PWD/.dependencies/ninja-1.12.1/ninja \
|
||||
--build-generator Ninja \
|
||||
--build-target tests \
|
||||
--test-command ctest
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@ set(PROJECT_VERSION_MINOR
|
|||
CACHE STRING "Project minor version" FORCE
|
||||
)
|
||||
set(PROJECT_VERSION_REV
|
||||
3
|
||||
4
|
||||
CACHE STRING "Project revision" FORCE
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue