diff --git a/fantemp/bootloader.cpp b/fantemp/bootloader.cpp new file mode 100644 index 0000000..b57b4c3 --- /dev/null +++ b/fantemp/bootloader.cpp @@ -0,0 +1,46 @@ +#include "bootloader.hpp" + +#include +#include +#include + +namespace { + +typedef void (*jmp_fn)() __attribute__((noreturn)); + +jmp_fn boot = reinterpret_cast(0x0000); +jmp_fn bootloader = reinterpret_cast(0x7E00 / 2); + +} // namespace + +bool Bootloader::handleReset() +{ + wdt_reset(); + uint8_t mcuStatus = MCUSR; + MCUSR &= ~(1 << WDRF); + wdt_disable(); + return (mcuStatus & (1 << WDRF)); +} + +void Bootloader::reset() +{ + wdt_enable(WDTO_15MS); + while (true) + ; +} + +bool Bootloader::check() +{ + if (pgm_read_byte(reinterpret_cast(bootloader) * 2) == 0xF8) + return true; + + return false; +} + +void Bootloader::call() +{ + if (check()) + bootloader(); + else + boot(); +} diff --git a/fantemp/bootloader.hpp b/fantemp/bootloader.hpp new file mode 100644 index 0000000..8f7574d --- /dev/null +++ b/fantemp/bootloader.hpp @@ -0,0 +1,24 @@ +#pragma once + +class Bootloader { + public: + template + static inline void init(Fn callback) + { + if (handleReset()) { + callback(); + call(); + } + } + + static inline void enter() + { + reset(); + } + + private: + static bool handleReset(); + static void reset(); + static bool check(); + static void call(); +}; diff --git a/fantemp/fantemp.cppproj b/fantemp/fantemp.cppproj index ae268db..7124ef1 100644 --- a/fantemp/fantemp.cppproj +++ b/fantemp/fantemp.cppproj @@ -200,6 +200,12 @@ compile + + compile + + + compile + compile diff --git a/fantemp/main.cpp b/fantemp/main.cpp index d37fafa..43749c8 100644 --- a/fantemp/main.cpp +++ b/fantemp/main.cpp @@ -5,11 +5,14 @@ #define UART0_INT_VECTORS #include "uart/hardware0.hpp" +#include "bootloader.hpp" #include "controller.hpp" #include "terminal.hpp" int main() { + Bootloader::init([]() {}); + using serial = uart::Uart0>; Terminal terminal; terminal.init(); diff --git a/fantemp/terminal.hpp b/fantemp/terminal.hpp index 19739bc..87f6e24 100644 --- a/fantemp/terminal.hpp +++ b/fantemp/terminal.hpp @@ -15,6 +15,7 @@ GF(HELP_CMD, "help"); GF(SHOW_CMD, "show"); GF(CURVE_CMD, "curve"); GF(MONITOR_CMD, "monitor"); +GF(BOOTLOADER_CMD, "bootloader"); constexpr auto BACKSPACE = uint8_t{0x7f}; constexpr auto CTRL_C = uint8_t{0x03}; @@ -109,6 +110,8 @@ class Terminal { printCurve(); } else if (strncmp_P(m_inputBuffer, reinterpret_cast(MONITOR_CMD), m_inputSize) == 0) { m_state = State::MONITOR; + } else if (strncmp_P(m_inputBuffer, reinterpret_cast(BOOTLOADER_CMD), m_inputSize) == 0) { + handleBootloader(); } else { printUnknown(); } @@ -124,10 +127,11 @@ class Terminal { static void printHelp() { m_serial << F("FanTemp command overview: ") << ENDL; - m_serial << HELP_CMD << F(" ....: print this help message") << ENDL; - m_serial << SHOW_CMD << F(" ....: shows current temperature and fan speed") << ENDL; - m_serial << CURVE_CMD << F(" ...: shows the curve used to map temperature to fan speed") << ENDL; - m_serial << MONITOR_CMD << F(" .: loops the show command until Ctrl + C is pressed") << ENDL; + m_serial << HELP_CMD << F(" .......: print this help message") << ENDL; + m_serial << SHOW_CMD << F(" .......: shows current temperature and fan speed") << ENDL; + m_serial << CURVE_CMD << F(" ......: shows the curve used to map temperature to fan speed") << ENDL; + m_serial << MONITOR_CMD << F(" ....: loops the show command until Ctrl + C is pressed") << ENDL; + m_serial << BOOTLOADER_CMD << F(" .: enters the bootloader after 10 seconds") << ENDL; } static void showState() @@ -156,6 +160,21 @@ class Terminal { } } + static void handleBootloader() + { + for (int8_t i = 10; i >= 0; --i) { + m_serial << i << ENDL; + _delay_ms(1000); + } + + m_serial << F("Entering bootloader...") << ENDL; + m_serial.flushTx(); + + _delay_ms(1000); + + Bootloader::enter(); + } + static void printUnknown() { m_serial << F("Unknown command \"");