WIP CAN flashing support
This commit is contained in:
5
config_partition.h
Normal file
5
config_partition.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#include "ab_boot/ab_boot.h"
|
||||||
|
|
||||||
|
struct config_partition {
|
||||||
|
struct ab_boot_config ab_boot_config;
|
||||||
|
};
|
96
flasher/CANFlasher.hpp
Normal file
96
flasher/CANFlasher.hpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "Flasher.hpp"
|
||||||
|
|
||||||
|
namespace can_flasher {
|
||||||
|
namespace {
|
||||||
|
flasher::Result handle_start(uint8_t *data, size_t length) {
|
||||||
|
uintptr_t address;
|
||||||
|
if (length != sizeof(address))
|
||||||
|
return flasher::Result::InvalidParameter;
|
||||||
|
|
||||||
|
memcpy(&address, data, sizeof(address));
|
||||||
|
return flasher::start(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
flasher::Result handle_write(uint8_t *data, size_t length) {
|
||||||
|
return flasher::write(data, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle(uint8_t *data, size_t length) {
|
||||||
|
using enum flasher::State;
|
||||||
|
|
||||||
|
if (length < 1) {
|
||||||
|
flasher::flasher_state_callback(flasher::get_state(), flasher::Result::InvalidParameter, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flasher::State target_state = (flasher::State)*data;
|
||||||
|
data += 1;
|
||||||
|
length -= 1;
|
||||||
|
|
||||||
|
flasher::Result result;
|
||||||
|
switch (target_state) {
|
||||||
|
case Idle:
|
||||||
|
flasher::init();
|
||||||
|
result = flasher::Result::Success;
|
||||||
|
break;
|
||||||
|
case Erasing:
|
||||||
|
result = handle_start(data, length);
|
||||||
|
break;
|
||||||
|
case Writing:
|
||||||
|
result = handle_write(data, length);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = flasher::Result::InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
flasher::flasher_state_callback(flasher::get_state(), result, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t FeedbackSize =
|
||||||
|
sizeof(flasher::State) + sizeof(flasher::Result) + sizeof(uint32_t);
|
||||||
|
static_assert(FeedbackSize <= 8);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint32_t last_sent;
|
||||||
|
std::atomic<bool> updated;
|
||||||
|
bool valid;
|
||||||
|
uint8_t data[FeedbackSize];
|
||||||
|
} feedback;
|
||||||
|
|
||||||
|
void generate_feedback(flasher::State state, flasher::Result result, uint32_t arg) {
|
||||||
|
uint8_t *ptr = feedback.data;
|
||||||
|
std::memcpy(ptr, &state, sizeof(state));
|
||||||
|
ptr += sizeof(state);
|
||||||
|
std::memcpy(ptr, &result, sizeof(result));
|
||||||
|
ptr += sizeof(result);
|
||||||
|
std::memcpy(ptr, &arg, sizeof(arg));
|
||||||
|
// this works because poll_feedback can't interrupt us
|
||||||
|
feedback.updated.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool poll_feedback(uint32_t now, uint8_t *out) {
|
||||||
|
if (feedback.updated.load() ||
|
||||||
|
(feedback.valid && now - feedback.last_sent >= 500)) {
|
||||||
|
feedback.valid = true;
|
||||||
|
do {
|
||||||
|
feedback.updated.store(false);
|
||||||
|
std::memcpy(out, feedback.data, sizeof(feedback.data));
|
||||||
|
// this works because we cannot interrupt generate_feedback
|
||||||
|
} while (feedback.updated.load());
|
||||||
|
feedback.last_sent = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace flasher {
|
||||||
|
void flasher_state_callback(flasher::State state, flasher::Result result, uint32_t arg) {
|
||||||
|
can_flasher::generate_feedback(state, result, arg);
|
||||||
|
}
|
||||||
|
}
|
203
flasher/Flasher.hpp
Normal file
203
flasher/Flasher.hpp
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "stm32f1xx_hal.h"
|
||||||
|
|
||||||
|
#include "ab_boot/ab_boot.h"
|
||||||
|
#include "stm32f1xx_hal_def.h"
|
||||||
|
#include "stm32f1xx_hal_flash.h"
|
||||||
|
#include "stm32f1xx_hal_flash_ex.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace flasher {
|
||||||
|
enum class State : uint8_t {
|
||||||
|
Idle,
|
||||||
|
Erasing,
|
||||||
|
Waiting,
|
||||||
|
Writing,
|
||||||
|
Error
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
enum class FlashRegion {
|
||||||
|
Invalid,
|
||||||
|
Bootloader,
|
||||||
|
AppA,
|
||||||
|
AppB,
|
||||||
|
Config
|
||||||
|
};
|
||||||
|
|
||||||
|
FlashRegion region_for_address(uintptr_t address) {
|
||||||
|
using enum FlashRegion;
|
||||||
|
|
||||||
|
if (address >= FLASH_START && address < APP_A_START) {
|
||||||
|
return Bootloader;
|
||||||
|
} else if (address >= APP_A_START && address < (APP_A_START + APP_SIZE)) {
|
||||||
|
return AppA;
|
||||||
|
} else if (address >= APP_B_START && address < (APP_B_START + APP_SIZE)) {
|
||||||
|
return AppB;
|
||||||
|
} else if (address >= CONFIG_START && address < (CONFIG_START + CONFIG_SIZE)) {
|
||||||
|
return Config;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t region_size(FlashRegion region) {
|
||||||
|
using enum FlashRegion;
|
||||||
|
|
||||||
|
switch (region) {
|
||||||
|
case Bootloader:
|
||||||
|
return AB_BOOT_SIZE;
|
||||||
|
case AppA:
|
||||||
|
case AppB:
|
||||||
|
return APP_SIZE;
|
||||||
|
case Config:
|
||||||
|
return CONFIG_SIZE;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid_start_address(uint32_t address) {
|
||||||
|
return address == FLASH_START || address == APP_A_START ||
|
||||||
|
address == APP_B_START || address == CONFIG_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
static State state_;
|
||||||
|
static FlashRegion region_;
|
||||||
|
static uintptr_t address_;
|
||||||
|
static uint8_t write_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Result : uint8_t {
|
||||||
|
Success,
|
||||||
|
RegionNotAllowed,
|
||||||
|
InvalidParameter,
|
||||||
|
InvalidState,
|
||||||
|
WriteError,
|
||||||
|
InProgress
|
||||||
|
};
|
||||||
|
|
||||||
|
void flasher_state_callback(State state, Result result, uint32_t arg);
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
state_ = State::Idle;
|
||||||
|
region_ = FlashRegion::Invalid;
|
||||||
|
address_ = 0;
|
||||||
|
write_size_ = 0;
|
||||||
|
HAL_FLASH_Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result start(uintptr_t address) {
|
||||||
|
if (state_ != State::Idle)
|
||||||
|
return Result::InvalidState;
|
||||||
|
|
||||||
|
if (!is_valid_start_address(address)) {
|
||||||
|
return Result::InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlashRegion flashed_region = region_for_address(address);
|
||||||
|
FlashRegion running_region = region_for_address((uintptr_t)&start);
|
||||||
|
|
||||||
|
if (flashed_region == FlashRegion::Invalid) {
|
||||||
|
return Result::InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flashed_region == running_region) {
|
||||||
|
// prohibit flashing the currently running app
|
||||||
|
return Result::RegionNotAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = region_size(flashed_region);
|
||||||
|
FLASH_EraseInitTypeDef ferase = {
|
||||||
|
.TypeErase = FLASH_TYPEERASE_PAGES,
|
||||||
|
.PageAddress = address,
|
||||||
|
.NbPages = size / FLASH_PAGE_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
HAL_FLASH_Unlock();
|
||||||
|
if (HAL_FLASHEx_Erase_IT(&ferase) != HAL_OK) {
|
||||||
|
return Result::WriteError;
|
||||||
|
}
|
||||||
|
|
||||||
|
state_ = State::Erasing;
|
||||||
|
region_ = flashed_region;
|
||||||
|
address_ = address;
|
||||||
|
|
||||||
|
return Result::InProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result write(uint8_t *data, uint8_t length) {
|
||||||
|
if (state_ != State::Waiting)
|
||||||
|
return Result::InvalidState;
|
||||||
|
|
||||||
|
uint32_t program_type;
|
||||||
|
switch (length) {
|
||||||
|
case 2:
|
||||||
|
program_type = FLASH_PROC_PROGRAMHALFWORD;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
program_type = FLASH_PROC_PROGRAMWORD;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
program_type = FLASH_PROC_PROGRAMDOUBLEWORD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Result::InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t data_int = 0;
|
||||||
|
memcpy(&data_int, data, length);
|
||||||
|
|
||||||
|
if (HAL_FLASH_Program_IT(program_type, address_, data_int) != HAL_OK)
|
||||||
|
return Result::WriteError;
|
||||||
|
|
||||||
|
state_ = State::Writing;
|
||||||
|
write_size_ = length;
|
||||||
|
|
||||||
|
return Result::InProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
State get_state() {
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flash_callback(bool success) {
|
||||||
|
using enum State;
|
||||||
|
|
||||||
|
// Ignore if we are in Idle state, could be the result of
|
||||||
|
// a cancelled operation.
|
||||||
|
if (state_ == Idle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
switch (state_) {
|
||||||
|
case Writing:
|
||||||
|
case Erasing:
|
||||||
|
address_ += write_size_;
|
||||||
|
flasher_state_callback(state_, Result::Success, address_);
|
||||||
|
state_ = Waiting;
|
||||||
|
write_size_ = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Spurious callback
|
||||||
|
HAL_FLASH_Lock();
|
||||||
|
state_ = Error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (state_) {
|
||||||
|
case Writing:
|
||||||
|
case Erasing:
|
||||||
|
flasher_state_callback(state_, Result::WriteError, address_);
|
||||||
|
[[fallthrough]];
|
||||||
|
default:
|
||||||
|
// Spurious callback
|
||||||
|
HAL_FLASH_Lock();
|
||||||
|
state_ = Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
main.cpp
58
main.cpp
@ -39,6 +39,8 @@
|
|||||||
#endif
|
#endif
|
||||||
#ifdef FEATURE_CAN
|
#ifdef FEATURE_CAN
|
||||||
#include "bobbycar-can.h"
|
#include "bobbycar-can.h"
|
||||||
|
#include "flasher/Flasher.hpp"
|
||||||
|
#include "flasher/CANFlasher.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -112,7 +114,7 @@ CAN_HandleTypeDef CanHandle;
|
|||||||
#define CANx_TX_IRQn USB_HP_CAN1_TX_IRQn
|
#define CANx_TX_IRQn USB_HP_CAN1_TX_IRQn
|
||||||
#define CANx_TX_IRQHandler USB_HP_CAN1_TX_IRQHandler
|
#define CANx_TX_IRQHandler USB_HP_CAN1_TX_IRQHandler
|
||||||
|
|
||||||
constexpr bool doDelayWithCanPoll = false;
|
constexpr bool doDelayWithCanPoll = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LOG_TO_SERIAL
|
#ifdef LOG_TO_SERIAL
|
||||||
@ -249,6 +251,7 @@ void sendFeedback();
|
|||||||
void parseCanCommand();
|
void parseCanCommand();
|
||||||
void applyIncomingCanMessage();
|
void applyIncomingCanMessage();
|
||||||
void sendCanFeedback();
|
void sendCanFeedback();
|
||||||
|
void sendFlasherFeedback();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FEATURE_BUTTON
|
#ifdef FEATURE_BUTTON
|
||||||
@ -379,6 +382,7 @@ int main()
|
|||||||
while ((HAL_GetTick() - tickstart) < wait)
|
while ((HAL_GetTick() - tickstart) < wait)
|
||||||
{
|
{
|
||||||
applyIncomingCanMessage();
|
applyIncomingCanMessage();
|
||||||
|
sendFlasherFeedback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -406,6 +410,7 @@ int main()
|
|||||||
parseCanCommand();
|
parseCanCommand();
|
||||||
|
|
||||||
sendCanFeedback();
|
sendCanFeedback();
|
||||||
|
sendFlasherFeedback();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FEATURE_BUTTON
|
#ifdef FEATURE_BUTTON
|
||||||
@ -450,6 +455,7 @@ void updateMotors()
|
|||||||
if (offsetcount < 2000) // calibrate ADC offsets
|
if (offsetcount < 2000) // calibrate ADC offsets
|
||||||
{
|
{
|
||||||
offsetcount++;
|
offsetcount++;
|
||||||
|
// TODO this is not an average
|
||||||
offsetrl1 = (adc_buffer.rl1 + offsetrl1) / 2;
|
offsetrl1 = (adc_buffer.rl1 + offsetrl1) / 2;
|
||||||
offsetrl2 = (adc_buffer.rl2 + offsetrl2) / 2;
|
offsetrl2 = (adc_buffer.rl2 + offsetrl2) / 2;
|
||||||
offsetrr1 = (adc_buffer.rr1 + offsetrr1) / 2;
|
offsetrr1 = (adc_buffer.rr1 + offsetrr1) / 2;
|
||||||
@ -1404,8 +1410,8 @@ void communicationTimeout()
|
|||||||
{
|
{
|
||||||
applyDefaultSettings();
|
applyDefaultSettings();
|
||||||
|
|
||||||
buzzer.freq = 24;
|
//buzzer.freq = 24;
|
||||||
buzzer.pattern = 1;
|
//buzzer.pattern = 1;
|
||||||
|
|
||||||
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
|
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
|
||||||
}
|
}
|
||||||
@ -1767,6 +1773,31 @@ void sendCanFeedback()
|
|||||||
|
|
||||||
arr[whichToSend++]();
|
arr[whichToSend++]();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendFlasherFeedback() {
|
||||||
|
using bobbycar::protocol::can::MotorController;
|
||||||
|
static CAN_TxHeaderTypeDef header = {
|
||||||
|
.StdId = MotorController<isBackBoard, false>::Command::FlasherCtrl,
|
||||||
|
.ExtId = 0,
|
||||||
|
.IDE = CAN_ID_STD,
|
||||||
|
.RTR = CAN_RTR_DATA,
|
||||||
|
.DLC = can_flasher::FeedbackSize,
|
||||||
|
.TransmitGlobalTime = DISABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
if (HAL_CAN_GetTxMailboxesFreeLevel(&CanHandle) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t buf[8];
|
||||||
|
if (!can_flasher::poll_feedback(HAL_GetTick(), buf))
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t TxMailbox;
|
||||||
|
if (const auto result = HAL_CAN_AddTxMessage(&CanHandle, &header, buf, &TxMailbox); result != HAL_OK) {
|
||||||
|
myPrintf("HAL_CAN_AddTxMessage() failed with %i", result);
|
||||||
|
//while (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FEATURE_BUTTON
|
#ifdef FEATURE_BUTTON
|
||||||
@ -2061,4 +2092,25 @@ extern "C" void CANx_TX_IRQHandler(void)
|
|||||||
using namespace bobbycar::controller;
|
using namespace bobbycar::controller;
|
||||||
HAL_CAN_IRQHandler(&CanHandle);
|
HAL_CAN_IRQHandler(&CanHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CAN flasher stuff
|
||||||
|
extern "C" void FLASH_IRQHandler(void)
|
||||||
|
{
|
||||||
|
HAL_FLASH_IRQHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue)
|
||||||
|
{
|
||||||
|
(void)ReturnValue;
|
||||||
|
|
||||||
|
flasher::flash_callback(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue)
|
||||||
|
{
|
||||||
|
(void)ReturnValue;
|
||||||
|
|
||||||
|
flasher::flash_callback(false);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user