#include "hal/cpu.h" #include "hal/adc.h" #include "hal/gpio.h" #include "hal/shr16.h" #include "hal/spi.h" #include "hal/usart.h" #include "pins.h" #include #include #include "modules/buttons.h" #include "modules/finda.h" #include "modules/fsensor.h" #include "modules/globals.h" #include "modules/idler.h" #include "modules/leds.h" #include "modules/protocol.h" #include "modules/selector.h" #include "logic/command_base.h" #include "logic/cut_filament.h" #include "logic/eject_filament.h" #include "logic/load_filament.h" #include "logic/no_command.h" #include "logic/tool_change.h" #include "logic/unload_filament.h" namespace mb = modules::buttons; namespace mp = modules::protocol; namespace mf = modules::finda; namespace mfs = modules::fsensor; namespace mi = modules::idler; namespace ml = modules::leds; namespace ms = modules::selector; namespace mg = modules::globals; namespace hu = hal::usart; /// Global instance of the protocol codec static mp::Protocol protocol; /// A command that resulted in the currently on-going operation logic::CommandBase *currentCommand = &logic::noCommand; /// remember the request message that started the currently running command mp::RequestMsg currentCommandRq(mp::RequestMsgCodes::unknown, 0); // examples and test code shall be located here void TmpPlayground() { using namespace hal; // SPI example // gpio::Init(gpio::GPIO_pin(GPIOC, 6), gpio::GPIO_InitTypeDef(gpio::Mode::output, gpio::Level::high)); // uint8_t dat[5]; // gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::low); // spi::TxRx(SPI0, 0x01); // spi::TxRx(SPI0, 0x00); // spi::TxRx(SPI0, 0x00); // spi::TxRx(SPI0, 0x00); // spi::TxRx(SPI0, 0x00); // gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::high); // gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::low); // dat[0] = spi::TxRx(SPI0, 0x00); // dat[1] = spi::TxRx(SPI0, 0x00); // dat[2] = spi::TxRx(SPI0, 0x00); // dat[3] = spi::TxRx(SPI0, 0x00); // dat[4] = spi::TxRx(SPI0, 0x00); // gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::high); // (void)dat; // using namespace hal::gpio; // WritePin(GPIO_pin(GPIOB, 5), Level::low); // TogglePin(GPIO_pin(GPIOB, 6)); // if (hal::gpio::ReadPin(GPIO_pin(GPIOB, 7)) == hal::gpio::Level::low) // break; sei(); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); hu::usart1.puts("1234567890\n"); } /// One-time setup of HW and SW components /// Called before entering the loop() function /// Green LEDs signalize the progress of initialization. If anything goes wrong we shall turn on a red LED void setup() { using namespace hal; cpu::Init(); mg::globals.Init(); // watchdog init shr16::shr16.Init(); ml::leds.SetMode(4, ml::Color::green, ml::Mode::blink0); ml::leds.Step(0); // @@TODO if the shift register doesn't work we really can't signalize anything, only internal variables will be accessible if the UART works hu::USART::USART_InitTypeDef usart_conf = { .rx_pin = gpio::GPIO_pin(GPIOD, 2), .tx_pin = gpio::GPIO_pin(GPIOD, 3), .baudrate = 115200, }; hu::usart1.Init(&usart_conf); ml::leds.SetMode(3, ml::Color::green, ml::Mode::on); ml::leds.Step(0); // @@TODO if both shift register and the UART are dead, we are sitting ducks :( spi::SPI_InitTypeDef spi_conf = { .miso_pin = gpio::GPIO_pin(TMC2130_SPI_MISO_PIN), .mosi_pin = gpio::GPIO_pin(TMC2130_SPI_MOSI_PIN), .sck_pin = gpio::GPIO_pin(TMC2130_SPI_SCK_PIN), .ss_pin = gpio::GPIO_pin(TMC2130_SPI_SS_PIN), .prescaler = 2, //4mhz .cpha = 1, .cpol = 1, }; spi::Init(SPI0, &spi_conf); ml::leds.SetMode(2, ml::Color::green, ml::Mode::on); ml::leds.Step(0); // tmc::Init() ml::leds.SetMode(1, ml::Color::green, ml::Mode::on); ml::leds.Step(0); // adc::Init(); ml::leds.SetMode(0, ml::Color::green, ml::Mode::on); ml::leds.Step(0); } void SendMessage(const mp::ResponseMsg &msg) { } void PlanCommand(const mp::RequestMsg &rq) { if (currentCommand->Error() == ErrorCode::OK) { // we are allowed to start a new command as the previous one is in the OK finished state switch (rq.code) { case mp::RequestMsgCodes::Cut: currentCommand = &logic::cutFilament; break; case mp::RequestMsgCodes::Eject: currentCommand = &logic::ejectFilament; break; case mp::RequestMsgCodes::Load: currentCommand = &logic::loadFilament; break; case mp::RequestMsgCodes::Tool: currentCommand = &logic::toolChange; break; case mp::RequestMsgCodes::Unload: currentCommand = &logic::unloadFilament; break; default: currentCommand = &logic::noCommand; break; } currentCommand->Reset(rq.value); } } void ReportRunningCommand() { mp::ResponseMsgParamCodes commandStatus; uint8_t value = 0; switch (currentCommand->Error()) { case ErrorCode::RUNNING: commandStatus = mp::ResponseMsgParamCodes::Processing; value = (uint8_t)currentCommand->State(); break; case ErrorCode::OK: commandStatus = mp::ResponseMsgParamCodes::Finished; break; default: commandStatus = mp::ResponseMsgParamCodes::Error; value = (uint8_t)currentCommand->Error(); break; } SendMessage(mp::ResponseMsg(currentCommandRq, commandStatus, value)); } void ProcessRequestMsg(const mp::RequestMsg &rq) { switch (rq.code) { case mp::RequestMsgCodes::Button: // behave just like if the user pressed a button break; case mp::RequestMsgCodes::Finda: // immediately report FINDA status SendMessage(mp::ResponseMsg(rq, mp::ResponseMsgParamCodes::Accepted, mf::finda.Pressed())); break; case mp::RequestMsgCodes::Mode: // immediately switch to normal/stealth as requested // modules::motion::SetMode(); break; case mp::RequestMsgCodes::Query: // immediately report progress of currently running command ReportRunningCommand(); break; case mp::RequestMsgCodes::Reset: // immediately reset the board - there is no response in this case break; // @@TODO case mp::RequestMsgCodes::Version: SendMessage(mp::ResponseMsg(rq, mp::ResponseMsgParamCodes::Accepted, 1)); // @@TODO case mp::RequestMsgCodes::Wait: break; // @@TODO case mp::RequestMsgCodes::Cut: case mp::RequestMsgCodes::Eject: case mp::RequestMsgCodes::Load: case mp::RequestMsgCodes::Tool: case mp::RequestMsgCodes::Unload: PlanCommand(rq); break; // case mp::RequestMsgCodes::SetVar: //@@TODO - this is where e.g. printer's fsensor gets updated // SetVar(rq); // break; default: // respond with an error message break; } } /// @returns true if a request was successfully finished bool CheckMsgs() { using mpd = mp::DecodeStatus; while (!hu::usart1.ReadEmpty()) { switch (protocol.DecodeRequest(hu::usart1.Read())) { case mpd::MessageCompleted: // process the input message return true; break; case mpd::NeedMoreData: // just continue reading break; case mpd::Error: // @@TODO what shall we do? Start some watchdog? We cannot send anything spontaneously break; } } return false; } /// Main loop of the firmware /// Proposed architecture /// checkMsgs(); /// if(msg is command){ /// activate command handling /// } else if(msg is query){ /// format response to query /// } /// StepCurrentCommand(); /// StepMotors(); /// StepLED(); /// StepWhateverElseNeedsStepping(); /// The idea behind the Step* routines is to keep each automaton non-blocking allowing for some “concurrency”. /// Some FW components will leverage ISR to do their stuff (UART, motor stepping?, etc.) void loop() { if (CheckMsgs()) { ProcessRequestMsg(protocol.GetRequestMsg()); } mb::buttons.Step(hal::adc::ReadADC(0)); ml::leds.Step(10); mf::finda.Step(0); mfs::fsensor.Step(0); mi::idler.Step(); ms::selector.Step(); currentCommand->Step(); // add a watchdog reset _delay_ms(10) } int main() { setup(); for (;;) { loop(); } return 0; }