Introduce Doxyfile + fix modules documentation

pull/29/head
D.R.racer 2021-06-25 11:39:37 +02:00
parent 3020f01062
commit c2a2c75be1
15 changed files with 2734 additions and 140 deletions

2494
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
///
/// Which state machines are high-level? Those which are being initiated either by a command over the serial line or from a button
/// - they report their progress to the printer
/// - they can be composed of other sub automatons
/// - they can be composed of other sub automata
namespace logic {

View File

@ -3,12 +3,12 @@
#include <stdint.h>
#include "debouncer.h"
/// Buttons are built on top of the raw ADC API
/// This layer should contain debouncing of buttons and their logical interpretation
namespace modules {
/// The buttons namespace provides all necessary facilities related to the logical model of the physical buttons device the MMU unit.
namespace buttons {
/// A model of a single button, performs automatic debouncing on top of the raw ADC API
struct Button : public debounce::Debouncer {
inline constexpr Button()
: debounce::Debouncer(debounce) {}
@ -18,12 +18,14 @@ private:
constexpr static const uint16_t debounce = 100;
};
/// Enum of buttons - used also as indices in an array of buttons to keep the code size tight.
enum {
Left = 0,
Middle,
Right
};
/// A model of the 3 buttons on the MMU unit
class Buttons {
constexpr static const uint8_t N = 3; ///< number of buttons currently supported
constexpr static const uint8_t adc = 1; ///< ADC index - will be some define or other constant later on
@ -31,11 +33,11 @@ class Buttons {
public:
inline constexpr Buttons() = default;
/// State machine step - reads the ADC, processes debouncing, updates states of individual buttons
/// Performs one step of the state machine - reads the ADC, processes debouncing, updates states of individual buttons
void Step();
/// @returns true if button at index is pressed
/// @@TODO add range checking if necessary
/// @param index of the button to check
inline bool ButtonPressed(uint8_t index) const { return buttons[index].Pressed(); }
/// @returns true if any of the button is pressed
@ -55,6 +57,7 @@ private:
static int8_t DecodeADC(uint16_t rawADC);
};
/// The one and only instance of Buttons in the FW
extern Buttons buttons;
} // namespace buttons

View File

@ -1,15 +1,17 @@
/// A generic debouncing algorithm
#pragma once
#include <stdint.h>
namespace modules {
/// The debounce namespace provides a generic debouncing algorithm.
namespace debounce {
/// Implements debouncing on 2-state logic variables (true/false, high/low, on/off, pressed/unpressed)
/// Intentionally not modelled as a template to avoid code bloat
class Debouncer {
public:
/// @param debounceTimeout initial debounce timeout in milliseconds @@TODO
/// - after what time of having a pressed level the debouncer considers the level stable enough to report the Pressed state.
inline constexpr Debouncer(uint8_t debounceTimeout)
: timeLastChange(0)
, debounceTimeout(debounceTimeout) {}

View File

@ -3,8 +3,11 @@
#include "debouncer.h"
namespace modules {
/// The finda namespace provides all necessary facilities related to the logical model of the FINDA device the MMU unit.
namespace finda {
/// A model of the FINDA - basically acts as a button with pre-set debouncing
class FINDA : protected debounce::Debouncer {
public:
/// time interval for debouncing @@TODO specify units
@ -14,10 +17,14 @@ public:
inline constexpr FINDA()
: debounce::Debouncer(debounce) {};
/// Performs one step of the state machine - reads the ADC, processes debouncing, updates states of FINDA
void Step();
using debounce::Debouncer::Pressed;
};
/// The one and only instance of FINDA in the FW
extern FINDA finda;
} // namespace finda

View File

@ -2,21 +2,25 @@
#include <stdint.h>
#include "debouncer.h"
/// External module - model of printer's fsensor
namespace modules {
/// The fsensor namespace provides all necessary facilities related to the logical model of the printer's filamens sensor device.
namespace fsensor {
/// the debouncer is probably not necessary, but it has all the necessary functionality for modelling of the fsensor
/// External module - model of printer's filament sensor
/// The debouncer is probably not necessary, but it has all the necessary functionality for modelling of the filament sensor
class FSensor : protected debounce::Debouncer {
public:
inline constexpr FSensor()
: debounce::Debouncer(debounce)
, reportedFSensorState(false) {};
/// Performs one step of the state machine - processes a change-of-state message if any arrived
void Step();
using debounce::Debouncer::Pressed;
/// Records a change of state of filament sensor when arrived via communication
void ProcessMessage(bool on);
private:
@ -25,7 +29,8 @@ private:
bool reportedFSensorState; ///< reported state that came from the printer via a communication message
};
/// The one and only instance of printer's filament sensor in the FW
extern FSensor fsensor;
} // namespace finda
} // namespace fsensor
} // namespace modules

View File

@ -2,16 +2,35 @@
#include <stdint.h>
namespace modules {
/// The globals namespace provides all necessary facilities related to keeping track of global state of the firmware.
namespace globals {
/// Globals keep track of global state variables in the firmware.
/// So far only Active slot and Filament loaded variables are used.
class Globals {
public:
/// Initializes the global storage hive - basically looks into EEPROM to gather information.
void Init();
/// @returns active filament slot on the MMU unit
/// Slots are numbered 0-4
uint8_t ActiveSlot() const;
/// Sets the active slot, usually after some command/operation.
/// Also updates the EEPROM records accordingly
/// @param newActiveSlot the new slot index to set
void SetActiveSlot(uint8_t newActiveSlot);
/// @returns true if filament is considered as loaded
/// @@TODO this may change meaning slightly as the MMU is primarily concerned
/// about whether a piece of filament is stock up of a PTFE tube.
/// If that's true, we cannot move the selector.
bool FilamentLoaded() const;
/// Sets the filament loaded flag value, usually after some command/operation.
/// Also updates the EEPROM records accordingly
/// @param newFilamentLoaded new state
void SetFilamentLoaded(bool newFilamentLoaded);
private:
@ -19,6 +38,7 @@ private:
bool filamentLoaded;
};
/// The one and only instance of global state variables
extern Globals globals;
} // namespace globals

View File

@ -1,15 +1,15 @@
#pragma once
#include <stdint.h>
/// Idler model
/// Handles asynchronnous Engaging / Disengaging operations
/// Keeps track of idler's current state
namespace modules {
/// The idler namespace provides all necessary facilities related to the logical model of the idler device of the MMU unit.
namespace idler {
/// The Idler model handles asynchronnous Engaging / Disengaging operations and keeps track of idler's current state.
class Idler {
public:
/// Internal states of idler's state machine
enum {
Ready = 0, // intentionally set as zero in order to allow zeroing the Idler structure upon startup -> avoid explicit initialization code
Moving,
@ -23,15 +23,20 @@ public:
, currentSlot(0)
, currentlyEngaged(false) {}
// public operations on the idler
/// @retuns false in case an operation is already underway
/// Plan engaging of the idler to a specific filament slot
/// @param slot index to be activated
/// @returns false in case an operation is already underway
bool Engage(uint8_t slot);
/// @retuns false in case an operation is already underway
/// Plan disengaging of the idler, i.e. parking the idler
/// @returns false in case an operation is already underway
bool Disengage();
/// @retuns false in case an operation is already underway
/// Plan homing of the idler axis
/// @returns false in case an operation is already underway
bool Home();
/// Performs one step of the state machine according to currently planned operation
/// @returns true if the idler is ready to accept new commands (i.e. it has finished the last operation)
bool Step();
@ -62,6 +67,7 @@ private:
bool currentlyEngaged;
};
/// The one and only instance of Idler in the FW
extern Idler idler;
} // namespace idler

View File

@ -2,18 +2,22 @@
#include <stdint.h>
/// We have 5 pairs of LEDs
/// In each pair there is a green and a red LED
namespace modules {
/// @brief The leds namespace provides all necessary facilities related to the logical model of the sets of LEDs on the MMU unit.
///
/// We have 5 pairs of LEDs. In each pair there is a green and a red LED.
///
/// A typical scenario in the past was visualization of error states.
/// The combination of colors with blinking frequency had a specific meaning.
///
/// The physical connection is not important on this level (i.e. how and what shall be sent into the shift registers)
namespace modules {
/// The physical connection is not important on this level (i.e. how and what shall be sent into the shift registers).
///
/// LEDS are physically connected to a pair of shift registers along with some other signals.
/// The physical write operations are handled by hal::shr16.
namespace leds {
/// Mode of LED
/// Enum of LED modes
/// blink0 and blink1 allow for interlaced blinking of LEDs (one is on and the other off)
enum Mode {
off,
@ -22,20 +26,29 @@ enum Mode {
blink1 ///< start blinking at odd periods
};
/// Enum of LEDs color - green or red
enum Color {
green = 0,
red = 1
};
/// a single LED
/// A single LED
class LED {
public:
constexpr inline LED() = default;
/// Sets the mode of the LED
/// @param mode to set
void SetMode(leds::Mode mode);
/// @returns the currently active mode of the LED
inline leds::Mode Mode() const { return (leds::Mode)state.mode; }
/// @returns true if the LED shines
/// @param oddPeriod LEDs class operates this parameter based on blinking period based on elapsed real time
bool Step(bool oddPeriod);
/// @returns true if the LED shines
inline bool On() const { return state.on; }
private:
@ -50,7 +63,7 @@ private:
State state;
};
/// main LED API
/// The main LEDs API takes care of the whole set of LEDs
class LEDs {
public:
constexpr inline LEDs() = default;
@ -58,25 +71,46 @@ public:
/// step LED automaton
void Step();
/// @returns the number of LED pairs
inline constexpr uint8_t LedPairsCount() const { return ledPairs; }
/// Sets the mode of a LED in a pair
/// @param slot index of filament slot (index of the LED pair)
/// @param color green or red LED
/// @param mode to set
inline void SetMode(uint8_t slot, Color color, Mode mode) {
SetMode(slot * 2 + color, mode);
}
/// Sets the mode of a LED in a pair
/// @param index - raw index of the LED in the internal leds array
/// @param mode to set
inline void SetMode(uint8_t index, Mode mode) {
leds[index].SetMode(mode);
}
/// @returns the currently active mode of a LED in a pair
/// @param slot index of filament slot (index of the LED pair)
/// @param color green or red LED
inline leds::Mode Mode(uint8_t slot, Color color) {
return Mode(slot * 2 + color);
}
/// @returns the currently active mode of a LED
/// @param index - raw index of the LED in the internal leds array
inline leds::Mode Mode(uint8_t index) {
return leds[index].Mode();
}
/// @returns true if a LED is shining
/// @param index - raw index of the LED in the internal leds array
inline bool LedOn(uint8_t index) const {
return leds[index].On();
}
/// @returns true if a LED is shining
/// @param slot index of filament slot (index of the LED pair)
/// @param color green or red LED
inline bool LedOn(uint8_t slot, Color color) const {
return leds[slot * 2 + color].On();
}
@ -97,6 +131,7 @@ private:
LED leds[ledPairs * 2];
};
/// The one and only instance of FINDA in the FW
extern LEDs leds;
} // namespace LEDs

View File

@ -1,4 +1,3 @@
/// @author Marek Bel
#include "permanent_storage.h"
#include "../hal/eeprom.h"
#include "globals.h"
@ -57,7 +56,6 @@ void Init() {
}
}
/// @brief Erase the whole EEPROM
void EraseAll() {
for (uint16_t i = 0; i < ee::EEPROM::End(); i++) {
ee::EEPROM::UpdateByte(i, static_cast<uint8_t>(eepromEmpty));

View File

@ -1,98 +1,101 @@
/// Permanent storage implementation
/// This is the logic/wear levelling/data structure on top of the raw EEPROM API
/// @author Marek Bel
/// Extracted and refactored from MM-control-01
#pragma once
#include "../hal/eeprom.h"
namespace modules {
/// @brief The permanent_storage namespace provides all necessary facilities related to permanently storing data (into EEPROM) on the MMU unit.
///
/// It uses some clever logic/wear levelling/data structure on top of the raw EEPROM API.
/// The code was originally written by Marek Bel for the previous MMU firmware.
namespace permanent_storage {
void Init();
void EraseAll();
/// Initialization of the permanent storage hive
void Init();
/// @brief Read manipulate and store bowden length
///
/// Value is stored independently for each filament.
/// Active filament is deduced from active_extruder global variable.
class BowdenLength {
public:
static uint16_t get();
static const uint8_t stepSize = 10u; ///< increase()/decrease() bowden length step size
BowdenLength();
bool increase();
bool decrease();
~BowdenLength();
/// Erase the whole EEPROM
void EraseAll();
private:
uint8_t filament; ///< Selected filament
uint16_t length; ///< Selected filament bowden length
/// @brief Read manipulate and store bowden length
///
/// Value is stored independently for each filament.
/// Active filament is deduced from active_extruder global variable.
class BowdenLength {
public:
static uint16_t get();
static const uint8_t stepSize = 10u; ///< increase()/decrease() bowden length step size
BowdenLength();
bool increase();
bool decrease();
~BowdenLength();
private:
uint8_t filament; ///< Selected filament
uint16_t length; ///< Selected filament bowden length
};
/// @brief Read and store last filament loaded to nozzle
///
/// 800(data) + 3(status) EEPROM cells are used to store 4 bit value frequently
/// to spread wear between more cells to increase durability.
///
/// Expected worst case durability scenario:
/// @n Print has 240mm height, layer height is 0.1mm, print takes 10 hours,
/// filament is changed 5 times each layer, EEPROM endures 100 000 cycles
/// @n Cell written per print: 240/0.1*5/800 = 15
/// @n Cell written per hour : 15/10 = 1.5
/// @n Fist cell failure expected: 100 000 / 1.5 = 66 666 hours = 7.6 years
///
/// Algorithm can handle one cell failure in status and one cell in data.
/// Status use 2 of 3 majority vote.
/// If bad data cell is detected, status is switched to next key.
/// Key alternates between begin to end and end to begin write order.
/// Two keys are needed for each start point and direction.
/// If two data cells fails, area between them is unavailable to write.
/// If this is first and last cell, whole storage is disabled.
/// This vulnerability can be avoided by adding additional keys
/// and start point in the middle of the EEPROM.
///
/// It would be possible to implement twice as efficient algorithm, if
/// separate EEPROM erase and EEPROM write commands would be available and
/// if write command would allow to be invoked twice between erases to update
/// just one nibble. Such commands are not available in AVR Libc, and possibility
/// to use write command twice is not documented in atmega32U4 datasheet.
///
class FilamentLoaded {
public:
static bool get(uint8_t &filament);
static bool set(uint8_t filament);
private:
enum Key {
KeyFront1,
KeyReverse1,
KeyFront2,
KeyReverse2,
BehindLastKey,
};
static_assert(BehindLastKey - 1 <= 0xf, "Key doesn't fit into a nibble.");
static uint8_t getStatus();
static bool setStatus(uint8_t status);
static int16_t getIndex();
static void getNext(uint8_t &status, int16_t &index);
static void getNext(uint8_t &status);
};
/// @brief Read and store last filament loaded to nozzle
///
/// 800(data) + 3(status) EEPROM cells are used to store 4 bit value frequently
/// to spread wear between more cells to increase durability.
///
/// Expected worst case durability scenario:
/// @n Print has 240mm height, layer height is 0.1mm, print takes 10 hours,
/// filament is changed 5 times each layer, EEPROM endures 100 000 cycles
/// @n Cell written per print: 240/0.1*5/800 = 15
/// @n Cell written per hour : 15/10 = 1.5
/// @n Fist cell failure expected: 100 000 / 1.5 = 66 666 hours = 7.6 years
///
/// Algorithm can handle one cell failure in status and one cell in data.
/// Status use 2 of 3 majority vote.
/// If bad data cell is detected, status is switched to next key.
/// Key alternates between begin to end and end to begin write order.
/// Two keys are needed for each start point and direction.
/// If two data cells fails, area between them is unavailable to write.
/// If this is first and last cell, whole storage is disabled.
/// This vulnerability can be avoided by adding additional keys
/// and start point in the middle of the EEPROM.
///
/// It would be possible to implement twice as efficient algorithm, if
/// separate EEPROM erase and EEPROM write commands would be available and
/// if write command would allow to be invoked twice between erases to update
/// just one nibble. Such commands are not available in AVR Libc, and possibility
/// to use write command twice is not documented in atmega32U4 datasheet.
///
class FilamentLoaded {
public:
static bool get(uint8_t &filament);
static bool set(uint8_t filament);
/// @brief Read and increment drive errors
///
/// (Motor power rail voltage loss)
class DriveError {
public:
static uint16_t get();
static void increment();
private:
enum Key {
KeyFront1,
KeyReverse1,
KeyFront2,
KeyReverse2,
BehindLastKey,
};
static_assert(BehindLastKey - 1 <= 0xf, "Key doesn't fit into a nibble.");
static uint8_t getStatus();
static bool setStatus(uint8_t status);
static int16_t getIndex();
static void getNext(uint8_t &status, int16_t &index);
static void getNext(uint8_t &status);
};
/// @brief Read and increment drive errors
///
/// (Motor power rail voltage loss)
class DriveError {
public:
static uint16_t get();
static void increment();
private:
static uint8_t getL();
static void setL(uint8_t lowByte);
static uint8_t getH();
static void setH(uint8_t highByte);
};
private:
static uint8_t getL();
static void setL(uint8_t lowByte);
static uint8_t getH();
static void setH(uint8_t highByte);
};
} // namespace permanent_storage
} // namespace modules

View File

@ -1,14 +1,15 @@
#pragma once
#include <stdint.h>
/// MMU protocol implementation
namespace modules {
/// @brief The MMU communication protocol implementation and related stuff.
///
/// See description of the new protocol in the MMU 2021 doc
/// @@TODO possibly add some checksum to verify the correctness of messages
namespace modules {
namespace protocol {
/// Definition of request message codes
enum class RequestMsgCodes : uint8_t {
unknown = 0,
Query = 'Q',
@ -25,6 +26,7 @@ enum class RequestMsgCodes : uint8_t {
Cut = 'K'
};
/// Definition of response message parameter codes
enum class ResponseMsgParamCodes : uint8_t {
unknown = 0,
Processing = 'P',
@ -34,30 +36,34 @@ enum class ResponseMsgParamCodes : uint8_t {
Rejected = 'R'
};
/// A request message
/// Requests are being sent by the printer into the MMU
/// It is the same structure as the generic Msg
/// A request message - requests are being sent by the printer into the MMU.
struct RequestMsg {
RequestMsgCodes code;
uint8_t value;
RequestMsgCodes code; ///< code of the request message
uint8_t value; ///< value of the request message
/// @param code of the request message
/// @param value of the request message
inline RequestMsg(RequestMsgCodes code, uint8_t value)
: code(code)
, value(value) {}
};
/// A response message
/// Responses are being sent from the MMU into the printer as a response to a request message
/// A response message - responses are being sent from the MMU into the printer as a response to a request message.
struct ResponseMsg {
RequestMsg request; ///< response is always preceeded by the request message
ResponseMsgParamCodes paramCode; ///< parameters of reply
uint8_t paramValue; ///< parameters of reply
ResponseMsgParamCodes paramCode; ///< code of the parameter
uint8_t paramValue; ///< value of the parameter
/// @param request the source request message this response is a reply to
/// @param paramCode code of the parameter
/// @param paramValue value of the parameter
inline ResponseMsg(RequestMsg request, ResponseMsgParamCodes paramCode, uint8_t paramValue)
: request(request)
, paramCode(paramCode)
, paramValue(paramValue) {}
};
/// Message decoding return value
/// Message decoding return values
enum class DecodeStatus : uint_fast8_t {
MessageCompleted, ///< message completed and successfully lexed
NeedMoreData, ///< message incomplete yet, waiting for another byte to come
@ -65,8 +71,9 @@ enum class DecodeStatus : uint_fast8_t {
};
/// Protocol class is responsible for creating/decoding messages in Rx/Tx buffer
///
/// Beware - in the decoding more, it is meant to be a statefull instance which works through public methods
/// processing one input byte per call
/// processing one input byte per call.
class Protocol {
public:
inline Protocol()
@ -91,18 +98,22 @@ public:
/// Encode generic response Command Accepted or Rejected
/// @param msg source request message for this response
/// @param ar code of response parameter
/// @param txbuff where to format the message
/// @returns number of bytes written into txbuff
static uint8_t EncodeResponseCmdAR(const RequestMsg &msg, ResponseMsgParamCodes ar, uint8_t *txbuff);
/// Encode response to Read FINDA query
/// @param msg source request message for this response
/// @param findaValue 1/0 (on/off) status of FINDA
/// @param txbuff where to format the message
/// @returns number of bytes written into txbuff
static uint8_t EncodeResponseReadFINDA(const RequestMsg &msg, uint8_t findaValue, uint8_t *txbuff);
/// Encode response to Version query
/// @param msg source request message for this response
/// @param value version number (0-255)
/// @param txbuff where to format the message
/// @returns number of bytes written into txbuff
static uint8_t EncodeResponseVersion(const RequestMsg &msg, uint8_t value, uint8_t *txbuff);
@ -110,6 +121,7 @@ public:
/// @param msg source request message for this response
/// @param code status of operation (Processing, Error, Finished)
/// @param value related to status of operation(e.g. error code or progress)
/// @param txbuff where to format the message
/// @returns number of bytes written into txbuff
static uint8_t EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMsgParamCodes code, uint8_t value, uint8_t *txbuff);

View File

@ -1,15 +1,15 @@
#pragma once
#include <stdint.h>
/// Selector model
/// Handles asynchronnous move operations between filament individual slots
/// Keeps track of selector's current state
namespace modules {
/// The selector namespace provides all necessary facilities related to the logical model of the selector device of the MMU unit.
namespace selector {
/// The selector model - handles asynchronnous move operations between filament individual slots and keeps track of selector's current state.
class Selector {
public:
/// Internal states of selector's state machine
enum {
Ready = 0,
Moving,
@ -21,13 +21,16 @@ public:
, plannedSlot(0)
, currentSlot(0) {}
// public operations on the selector
/// @retuns false in case an operation is already underway
/// Plan move of the selector to a specific filament slot
/// @param slot index to move to
/// @returns false in case an operation is already underway
bool MoveToSlot(uint8_t slot);
/// @retuns false in case an operation is already underway
/// Plan homing of the selector's axis
/// @returns false in case an operation is already underway
bool Home();
/// Performs one step of the state machine according to currently planned operation.
/// @returns true if the selector is ready to accept new commands (i.e. it has finished the last operation)
bool Step();
@ -51,6 +54,7 @@ private:
uint8_t currentSlot;
};
/// The one and only instance of Selector in the FW
extern Selector selector;
} // namespace selector

View File

@ -2,6 +2,8 @@
#include <stdint.h>
namespace modules {
/// The time namespace provides all necessary facilities related to measuring real elapsed time for the whole firmware.
namespace time {
/// A basic time tracking class
@ -11,6 +13,8 @@ class Timebase {
public:
constexpr inline Timebase()
: ms(0) {}
/// Initializes the Timebase class - sets the timers and prepares the internal variables.
void Init();
/// @returns current milliseconds elapsed from the initialization of this class
@ -22,6 +26,7 @@ private:
static void ISR();
};
/// The one and only instance of Selector in the FW
extern Timebase timebase;
} // namespace time

View File

@ -107,7 +107,7 @@ TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]")
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
// no changes to FINDA during unload - we'll pretend it never triggers
REQUIRE(!WhileCondition(
REQUIRE_FALSE(WhileCondition(
ff,
[&](int) { return mf::finda.Pressed(); },
50000));