/** * @file DCCGenerator.h * @brief NMRA DCC (Digital Command Control) signal generator * * Generates DCC protocol signals for controlling digital model locomotives. * Implements NMRA DCC standard with support for: * - Short addresses (1-127) and long addresses (128-10239) * - 128-step speed control * - Function control (F0-F12 implemented, expandable to F28) * * @note Requires external DCC booster circuit for track output * @author Locomotive Test Bench Project * @date 2025 */ #ifndef DCC_GENERATOR_H #define DCC_GENERATOR_H #include // Pin definitions for DCC output // These share the same pins as the motor controller (LM18200) // In DCC mode: GPIO 18 = DCC Signal A, GPIO 19 = DCC Signal B // In DC mode: GPIO 18 = PWM, GPIO 19 = Direction #define DCC_PIN_A 18 ///< DCC Signal A output pin (shared with MOTOR_PWM_PIN) #define DCC_PIN_B 19 ///< DCC Signal B output pin (shared with MOTOR_DIR_PIN) // DCC timing constants (microseconds) - NMRA standard #define DCC_ONE_BIT_TOTAL_DURATION_MAX 64 ///< Max duration for '1' bit #define DCC_ONE_BIT_TOTAL_DURATION_MIN 55 ///< Min duration for '1' bit #define DCC_ZERO_BIT_TOTAL_DURATION_MAX 10000 ///< Max duration for '0' bit #define DCC_ZERO_BIT_TOTAL_DURATION_MIN 95 ///< Min duration for '0' bit #define DCC_ONE_BIT_PULSE_DURATION 58 ///< Half-cycle for '1' bit (58μs) #define DCC_ZERO_BIT_PULSE_DURATION 100 ///< Half-cycle for '0' bit (100μs) /** * @class DCCGenerator * @brief DCC protocol signal generator * * Generates NMRA-compliant DCC signals for digital locomotive control. * Supports variable speed, direction, and function commands. * * @warning Output signals are low-power logic level. * Requires external booster circuit for track connection. */ class DCCGenerator { public: /** * @brief Constructor */ DCCGenerator(); /** * @brief Initialize DCC generator hardware * * Configures output pins to idle state. */ void begin(); /** * @brief Enable DCC signal generation * * Starts sending DCC packets to the track. */ void enable(); /** * @brief Disable DCC signal generation * * Stops DCC output and sets pins to safe state. */ void disable(); /** * @brief Set locomotive speed and direction * @param address DCC address (1-10239) * @param speed Speed value (0-100%) * @param direction Direction: 0 = reverse, 1 = forward */ void setLocoSpeed(uint16_t address, uint8_t speed, uint8_t direction); /** * @brief Control DCC function * @param address DCC address (1-10239) * @param function Function number (0-28) * @param state true = ON, false = OFF */ void setFunction(uint16_t address, uint8_t function, bool state); /** * @brief Update DCC signal generation * * Must be called regularly from main loop to send DCC packets. * Sends speed and function packets at appropriate intervals. */ void update(); /** * @brief Check if DCC is enabled * @return true if DCC mode is active */ bool isEnabled() { return enabled; } // Programming Track Methods /** * @brief Factory reset decoder (send CV8 = 8) * @return true if successful */ bool factoryReset(); /** * @brief Set decoder address * @param address New address (1-10239) * @return true if successful */ bool setDecoderAddress(uint16_t address); /** * @brief Read CV value from decoder * @param cv CV number (1-1024) * @param value Pointer to store read value * @return true if successful */ bool readCV(uint16_t cv, uint8_t* value); /** * @brief Write CV value to decoder * @param cv CV number (1-1024) * @param value Value to write (0-255) * @return true if successful */ bool writeCV(uint16_t cv, uint8_t value); private: bool enabled; ///< DCC generator enabled flag uint16_t currentAddress; ///< Current locomotive address uint8_t currentSpeed; ///< Current speed setting uint8_t currentDirection; ///< Current direction (0=rev, 1=fwd) uint32_t functionStates; ///< Function states bit field unsigned long lastPacketTime; ///< Timestamp of last packet sent static const unsigned long PACKET_INTERVAL = 30; ///< Packet interval (ms) // DCC packet construction and transmission /** * @brief Send a complete DCC packet * @param data Byte array containing packet data * @param length Number of bytes in packet */ void sendPacket(uint8_t* data, uint8_t length); /** * @brief Send a single DCC bit * @param value true = '1' bit, false = '0' bit */ void sendBit(bool value); /** * @brief Send DCC preamble (14 '1' bits) */ void sendPreamble(); /** * @brief Send a single byte * @param data Byte to send */ void sendByte(uint8_t data); /** * @brief Send speed command packet */ void sendSpeedPacket(); /** * @brief Send function group packet * @param group Function group number */ void sendFunctionPacket(uint8_t group); /** * @brief Calculate XOR checksum * @param data Data bytes * @param length Number of bytes * @return XOR checksum byte */ uint8_t calculateChecksum(uint8_t* data, uint8_t length); // Programming track helper methods /** * @brief Send service mode packet (programming track) * @param data Packet data bytes * @param length Number of bytes */ void sendServiceModePacket(uint8_t* data, uint8_t length); /** * @brief Verify byte write on programming track * @param cv CV number * @param value Expected value * @return true if ACK detected */ bool verifyByte(uint16_t cv, uint8_t value); /** * @brief Wait for ACK pulse from decoder * @return true if ACK detected within timeout */ bool waitForAck(); /** * @brief Calibrate ACS712 current sensor zero point * * Reads current sensor with no load to establish baseline. * Should be called during initialization. */ void calibrateCurrentSensor(); }; // Programming track current sensing threshold (mA) #define PROG_ACK_CURRENT_THRESHOLD 60 ///< Minimum ACK current (mA) #endif