From be1a88f4aba7ff2569153176b062cd3ae5363d19 Mon Sep 17 00:00:00 2001 From: BlackMark Date: Wed, 5 Aug 2026 21:44:47 +0200 Subject: [PATCH] Add unfinished extraction of stk500v2 protocol into library --- stk500v2/stk500v2.hpp | 344 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 stk500v2/stk500v2.hpp diff --git a/stk500v2/stk500v2.hpp b/stk500v2/stk500v2.hpp new file mode 100644 index 0000000..e0f5b86 --- /dev/null +++ b/stk500v2/stk500v2.hpp @@ -0,0 +1,344 @@ +#pragma once + +#include +#include +#include + +namespace stk500v2 { + +enum class Msg : uint8_t { + START = 0x1B, // ASCII ESC + TOKEN = 0x0E, +}; + +inline constexpr bool operator==(const Msg &msg, const uint8_t &num) +{ + return static_cast(msg) == num; +} + +enum class Cmd : uint8_t { + SIGN_ON = 0x01, + SET_PARAMETER = 0x02, + GET_PARAMETER = 0x03, + SET_DEVICE_PARAMETERS = 0x04, + OSCCAL = 0x05, + LOAD_ADDRESS = 0x06, + FIRMWARE_UPGRADE = 0x07, +}; + +inline constexpr bool operator==(const Cmd &cmd, const uint8_t &num) +{ + return static_cast(cmd) == num; +} + +enum class IspCmd : uint8_t { + ENTER_PROGMODE = 0x10, + LEAVE_PROGMODE = 0x11, + CHIP_ERASE = 0x12, + PROGRAM_FLASH = 0x13, + READ_FLASH = 0x14, + PROGRAM_EEPROM = 0x15, + READ_EEPROM = 0x16, + PROGRAM_FUSE = 0x17, + READ_FUSE = 0x18, + PROGRAM_LOCK = 0x19, + READ_LOCK = 0x1A, + READ_SIGNATURE = 0x1B, + READ_OSCCAL = 0x1C, + + SPI_MULTI = 0x1D, +}; + +enum class PpCmd : uint8_t { + ENTER_PROGMODE = 0x20, + LEAVE_PROGMODE = 0x21, + CHIP_ERASE = 0x22, + PROGRAM_FLASH = 0x23, + READ_FLASH = 0x24, + PROGRAM_EEPROM = 0x25, + READ_EEPROM = 0x26, + PROGRAM_FUSE = 0x27, + READ_FUSE = 0x28, + PROGRAM_LOCK = 0x29, + READ_LOCK = 0x2A, + READ_SIGNATURE = 0x2B, + READ_OSCCAL = 0x2C, + + SET_CONTROL_STACK = 0x2D, +}; + +enum class HvspCmd : uint8_t { + ENTER_PROGMODE = 0x30, + LEAVE_PROGMODE = 0x31, + CHIP_ERASE = 0x32, + PROGRAM_FLASH = 0x33, + READ_FLASH = 0x34, + PROGRAM_EEPROM = 0x35, + READ_EEPROM = 0x36, + PROGRAM_FUSE = 0x37, + READ_FUSE = 0x38, + PROGRAM_LOCK = 0x39, + READ_LOCK = 0x3A, + READ_SIGNATURE = 0x3B, + READ_OSCCAL = 0x3C, +}; + +enum class Status : uint8_t { + // Success + CMD_OK = 0x00, + + // Warnings + CMD_TOUT = 0x80, + RDY_BSY_TOUT = 0x81, + SET_PARAM_MISSING = 0x82, + + // Errors + CMD_FAILED = 0xC0, + CKSUM_ERROR = 0xC1, + CMD_UNKNOWN = 0xC9, +}; + +enum class Param : uint8_t { + BUILD_NUMBER_LOW = 0x80, + BUILD_NUMBER_HIGH = 0x81, + HW_VER = 0x90, + SW_MAJOR = 0x91, + SW_MINOR = 0x92, + VTARGET = 0x94, + VADJUST = 0x95, + OSC_PSCALE = 0x96, + OSC_CMATCH = 0x97, + SCK_DURATION = 0x98, + TOPCARD_DETECT = 0x9A, + STATUS = 0x9C, + DATA = 0x9D, + RESET_POLARITY = 0x9E, + CONTROLLER_INIT = 0x9F, +}; + +enum class Answer : uint8_t { + CKSUM_ERROR = 0xB0, +}; + +template +struct Message { + uint8_t start; + uint8_t number; + uint16_t size; + uint8_t token; + uint8_t body[Size]; + uint8_t checksum; +}; + +template +struct Callbacks { + static constexpr auto onSignOn = SignOnFn; + static constexpr auto onSetParam = SetParamFn; + static constexpr auto onGetParam = GetParamFn; +}; + +template +class Stk500v2 { + public: + inline void init() + { + m_serial.init(); + } + + inline bool callback() + { + if (receiveMessage()) { + handleMessage(); + } + + if (m_timeout) + return true; + + return false; + } + + private: + static constexpr auto MSG_BUFFER_SIZE = 275; + using msg_t = Message; + + msg_t m_msg; + Uart m_serial; + uint16_t m_timeout = Timeout; + + inline bool receiveByte(uint8_t &data) + { + constexpr auto MICROSECOND = 1000.0 * 1000; + constexpr auto SYMBOL_SIZE = 9; + constexpr auto BYTE_DELAY_US = (SYMBOL_SIZE * MICROSECOND) / Uart::BAUD_RATE; + constexpr auto NUM_MS_DELAY_STEPS = static_cast(round(1000 / BYTE_DELAY_US)); + uint16_t msDelay = NUM_MS_DELAY_STEPS; + + while (m_timeout) { + if (m_serial.rxByte(data)) { + m_timeout = Timeout; + return true; + } + + _delay_us(BYTE_DELAY_US); + + if (--msDelay == 0) { + msDelay = NUM_MS_DELAY_STEPS; + --m_timeout; + } + } + + return false; + } + + inline uint8_t calcChecksum() const + { + uint8_t checksum = static_cast(m_msg.start); + + for (uint16_t i = 1; i < 5 + m_msg.size; ++i) { + checksum ^= *(reinterpret_cast(&m_msg) + i); + } + + return checksum; + } + + inline bool receiveMessage() + { + constexpr auto CHECK_MESSAGE_FORMAT = true; + constexpr auto CHECK_MESSAGE_SIZE = true; + constexpr auto CHECK_MESSAGE_CHECKSUM = true; + + if (!receiveByte(m_msg.start)) { + return false; + } + if constexpr (CHECK_MESSAGE_FORMAT) { + if (m_msg.start != Msg::START) + return false; + } + + if (!receiveByte(m_msg.number)) + return false; + + if (!receiveByte(*(reinterpret_cast(&m_msg.size) + 1))) + return false; + if (!receiveByte(*reinterpret_cast(&m_msg.size))) + return false; + + if constexpr (CHECK_MESSAGE_SIZE) { + if (m_msg.size > sizeof(m_msg.body)) + return false; + } + + if (!receiveByte(m_msg.token)) + return false; + + if constexpr (CHECK_MESSAGE_FORMAT) { + if (m_msg.token != Msg::TOKEN) + return false; + } + + for (uint16_t i = 0; i < m_msg.size; ++i) { + if (!receiveByte(m_msg.body[i])) + return false; + } + + if (!receiveByte(m_msg.checksum)) + return false; + + if constexpr (CHECK_MESSAGE_CHECKSUM) { + if (m_msg.checksum != calcChecksum()) + return false; + } + + return true; + } + + inline void transmitMessage() const + { + m_serial.txByte(m_msg.start); + m_serial.txByte(m_msg.number); + m_serial.txByte(m_msg.size >> 8); + m_serial.txByte(m_msg.size & 0xFF); + m_serial.txByte(m_msg.token); + for (uint16_t i = 0; i < m_msg.size; ++i) + m_serial.txByte(m_msg.body[i]); + m_serial.txByte(m_msg.checksum); + } + + inline void handleMessage() + { + constexpr auto CHECK_MESSAGE_LENGTH = true; + + switch (m_msg.body[0]) { + case static_cast(Cmd::SIGN_ON): { + if constexpr (CHECK_MESSAGE_LENGTH) { + if (m_msg.size != 1) + break; + } + CallbackFns.onSignOn(); + sendReply(Cmd::SIGN_ON)>(); + break; + } + case static_cast(Cmd::SET_PARAMETER): { + if constexpr (CHECK_MESSAGE_LENGTH) { + if (m_msg.size != 3) + break; + } + CallbackFns.onSetParam(); + sendReply(Cmd::SET_PARAMETER)>(); + break; + } + case static_cast(Cmd::GET_PARAMETER): { + if constexpr (CHECK_MESSAGE_LENGTH) { + if (m_msg.size != 2) + break; + } + const auto errorOccurred = CallbackFns.onGetParam(static_cast(m_msg.body[1]), m_msg.body[2]); + sendReply(Cmd::GET_PARAMETER)>(errorOccurred); + break; + } + case static_cast(Cmd::SET_DEVICE_PARAMETERS): + break; + case static_cast(Cmd::OSCCAL): + break; + case static_cast(Cmd::LOAD_ADDRESS): + break; + case static_cast(Cmd::FIRMWARE_UPGRADE): + break; + } + } + + template + inline void sendReply(bool errorOccurred = false) + { + if constexpr (Command == Cmd::SIGN_ON) { + m_msg.size = 3 + 8; + m_msg.body[1] = static_cast(Status::CMD_OK); + m_msg.body[2] = 8; + m_msg.body[3] = 'S'; + m_msg.body[4] = 'T'; + m_msg.body[5] = 'K'; + m_msg.body[6] = '5'; + m_msg.body[7] = '0'; + m_msg.body[8] = '0'; + m_msg.body[9] = '_'; + m_msg.body[10] = '2'; + } else if (Command == Cmd::SET_PARAMETER) { + m_msg.size = 2; + m_msg.body[1] = static_cast(Status::CMD_OK); + } else if (Command == Cmd::GET_PARAMETER) { + if (!errorOccurred) { + m_msg.size = 3; + m_msg.body[1] = static_cast(Status::CMD_OK); + } else { + m_msg.size = 2; + m_msg.body[1] = static_cast(Status::CMD_FAILED); + } + } + + m_msg.checksum = calcChecksum(); + + transmitMessage(); + } +}; + +} // namespace stk500v2