288 lines
8.8 KiB
C++
288 lines
8.8 KiB
C++
#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 <avr/interrupt.h>
|
|
#include <util/delay.h>
|
|
|
|
#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;
|
|
}
|