Main thread using FreeRTOS notifications, added missing commands for firmware update

This commit is contained in:
Jan Käberich 2020-08-31 17:57:24 +02:00
parent 07ba714f1f
commit 8c33ae523a
11 changed files with 209 additions and 134 deletions

Binary file not shown.

View File

@ -157,6 +157,30 @@ bool Device::SendFirmwareChunk(Protocol::FirmwarePacket &fw)
} }
} }
bool Device::SendCommandWithoutPayload(Protocol::PacketType type)
{
if(m_connected) {
unsigned char buffer[32];
Protocol::PacketInfo p;
p.type = type;
unsigned int length = Protocol::EncodePacket(p, buffer, sizeof(buffer));
if(!length) {
qCritical() << "Failed to encode packet";
return false;
}
int actual_length;
auto ret = libusb_bulk_transfer(m_handle, EP_Data_Out_Addr, buffer, length, &actual_length, 0);
if(ret < 0) {
qCritical() << "Error sending data: "
<< libusb_strerror((libusb_error) ret);
return false;
}
return true;
} else {
return false;
}
}
std::vector<QString> Device::GetDevices() std::vector<QString> Device::GetDevices()
{ {
std::vector<QString> serials; std::vector<QString> serials;

View File

@ -48,6 +48,7 @@ public:
bool Configure(Protocol::SweepSettings settings); bool Configure(Protocol::SweepSettings settings);
bool SetManual(Protocol::ManualControl manual); bool SetManual(Protocol::ManualControl manual);
bool SendFirmwareChunk(Protocol::FirmwarePacket &fw); bool SendFirmwareChunk(Protocol::FirmwarePacket &fw);
bool SendCommandWithoutPayload(Protocol::PacketType type);
// Returns serial numbers of all connected devices // Returns serial numbers of all connected devices
static std::vector<QString> GetDevices(); static std::vector<QString> GetDevices();
QString serial() const; QString serial() const;

View File

@ -62,7 +62,7 @@ void FirmwareUpdateDialog::on_bStart_clicked()
} }
state = State::ErasingFLASH; state = State::ErasingFLASH;
addStatus("Erasing device memory..."); addStatus("Erasing device memory...");
// TODO issue write command dev.SendCommandWithoutPayload(Protocol::PacketType::ClearFlash);
timer.start(10000); timer.start(10000);
} }
@ -105,7 +105,7 @@ void FirmwareUpdateDialog::receivedAck()
// complete file transferred // complete file transferred
addStatus("Triggering device update..."); addStatus("Triggering device update...");
state = State::TriggeringUpdate; state = State::TriggeringUpdate;
// TODO trigger update dev.SendCommandWithoutPayload(Protocol::PacketType::PerformFirmwareUpdate);
timer.start(5000); timer.start(5000);
} }
sendNextFirmwareChunk(); sendNextFirmwareChunk();
@ -113,7 +113,7 @@ void FirmwareUpdateDialog::receivedAck()
break; break;
case State::TriggeringUpdate: case State::TriggeringUpdate:
addStatus("Rebooting device..."); addStatus("Rebooting device...");
// TODO listen for detected device // TODO delete current device and listen for reconnect
state = State::Idle; state = State::Idle;
break; break;
} }
@ -124,4 +124,5 @@ void FirmwareUpdateDialog::sendNextFirmwareChunk()
Protocol::FirmwarePacket fw; Protocol::FirmwarePacket fw;
fw.address = transferredBytes; fw.address = transferredBytes;
file->read((char*) &fw.data, Protocol::FirmwareChunkSize); file->read((char*) &fw.data, Protocol::FirmwareChunkSize);
dev.SendFirmwareChunk(fw);
} }

View File

@ -11,21 +11,22 @@
#include "USB/usb.h" #include "USB/usb.h"
#include "Flash.hpp" #include "Flash.hpp"
#include "Firmware.hpp" #include "Firmware.hpp"
#include "FreeRTOS.h"
#include "task.h"
#define LOG_LEVEL LOG_LEVEL_INFO #define LOG_LEVEL LOG_LEVEL_INFO
#define LOG_MODULE "App" #define LOG_MODULE "App"
#include "Log.h" #include "Log.h"
static volatile bool newResult;
static Protocol::Datapoint result; static Protocol::Datapoint result;
static volatile bool newSettings = false;
static Protocol::SweepSettings settings; static Protocol::SweepSettings settings;
static volatile bool newStatus = false;
static FPGA::SamplingResult statusResult; static FPGA::SamplingResult statusResult;
static volatile bool newManual = false;
static Protocol::ManualControl manual; static Protocol::ManualControl manual;
static Protocol::PacketInfo packet;
static TaskHandle_t handle;
// TODO set proper values // TODO set proper values
#define HW_REVISION 'A' #define HW_REVISION 'A'
#define FW_MAJOR 0 #define FW_MAJOR 0
@ -34,18 +35,34 @@ static Protocol::ManualControl manual;
extern SPI_HandleTypeDef hspi1; extern SPI_HandleTypeDef hspi1;
static Flash flash = Flash(&hspi1, FLASH_CS_GPIO_Port, FLASH_CS_Pin); static Flash flash = Flash(&hspi1, FLASH_CS_GPIO_Port, FLASH_CS_Pin);
void VNACallback(Protocol::Datapoint res) { #define FLAG_USB_PACKET 0x01
#define FLAG_DATAPOINT 0x02
#define FLAG_STATUSRESULT 0x04
static void VNACallback(Protocol::Datapoint res) {
result = res; result = res;
newResult = true; BaseType_t woken = false;
xTaskNotifyFromISR(handle, FLAG_DATAPOINT, eSetBits, &woken);
portYIELD_FROM_ISR(woken);
} }
void VNAStatusCallback(FPGA::SamplingResult res) { static void VNAStatusCallback(FPGA::SamplingResult res) {
statusResult = res; statusResult = res;
newStatus = true; BaseType_t woken = false;
xTaskNotifyFromISR(handle, FLAG_STATUSRESULT, eSetBits, &woken);
portYIELD_FROM_ISR(woken);
}
static void USBPacketReceived(Protocol::PacketInfo p) {
packet = p;
BaseType_t woken = false;
xTaskNotifyFromISR(handle, FLAG_USB_PACKET, eSetBits, &woken);
portYIELD_FROM_ISR(woken);
} }
void App_Start() { void App_Start() {
handle = xTaskGetCurrentTaskHandle();
usb_init(communication_usb_input); usb_init(communication_usb_input);
Log_Init(); Log_Init();
Communication::SetCallback(USBPacketReceived);
// Pass on logging output to USB // Pass on logging output to USB
Log_SetRedirect(usb_log); Log_SetRedirect(usb_log);
LOG_INFO("Start"); LOG_INFO("Start");
@ -66,66 +83,143 @@ void App_Start() {
if (!VNA::Init()) { if (!VNA::Init()) {
LOG_CRIT("Initialization failed, unable to start"); LOG_CRIT("Initialization failed, unable to start");
} }
// Allow USB enumeration
// USB_EN_GPIO_Port->BSRR = USB_EN_Pin;
uint32_t lastNewPoint = HAL_GetTick(); uint32_t lastNewPoint = HAL_GetTick();
bool sweepActive = false; bool sweepActive = false;
while (1) { while (1) {
if (newResult) { uint32_t notification;
Protocol::PacketInfo packet; if(xTaskNotifyWait(0x00, UINT32_MAX, &notification, 100) == pdPASS) {
packet.type = Protocol::PacketType::Datapoint; // something happened
packet.datapoint = result; if(notification & FLAG_DATAPOINT) {
Communication::Send(packet); Protocol::PacketInfo packet;
newResult = false; packet.type = Protocol::PacketType::Datapoint;
lastNewPoint = HAL_GetTick(); packet.datapoint = result;
if(result.pointNum == settings.points - 1) {
// end of sweep
// read PLL temperatures
uint8_t tempSource, tempLO;
VNA::GetTemps(&tempSource, &tempLO);
LOG_INFO("PLL temperatures: %u/%u", tempSource, tempLO);
// Read ADC min/max
auto limits = FPGA::GetADCLimits();
#define ADC_LIMIT 30000
// Compile info packet
packet.type = Protocol::PacketType::DeviceInfo;
packet.info.FPGA_configured = 1;
if(limits.P1min < -ADC_LIMIT || limits.P1max > ADC_LIMIT
|| limits.P2min < -ADC_LIMIT || limits.P2max > ADC_LIMIT
|| limits.Rmin < -ADC_LIMIT || limits.Rmax > ADC_LIMIT) {
packet.info.ADC_overload = true;
} else {
packet.info.ADC_overload = false;
}
packet.info.FW_major = FW_MAJOR;
packet.info.FW_minor = FW_MINOR;
packet.info.HW_Revision = HW_REVISION;
auto status = FPGA::GetStatus();
packet.info.LO1_locked = (status & (int) FPGA::Interrupt::LO1Unlock) ? 0 : 1;
packet.info.source_locked = (status & (int) FPGA::Interrupt::SourceUnlock) ? 0 : 1;
packet.info.extRefAvailable = 0;
packet.info.extRefInUse = 0;
packet.info.temperatures.LO1 = tempLO;
packet.info.temperatures.source = tempSource;
packet.info.temperatures.MCU = 0;
Communication::Send(packet); Communication::Send(packet);
lastNewPoint = HAL_GetTick();
if(result.pointNum == settings.points - 1) {
// end of sweep
// read PLL temperatures
uint8_t tempSource, tempLO;
VNA::GetTemps(&tempSource, &tempLO);
LOG_INFO("PLL temperatures: %u/%u", tempSource, tempLO);
// Read ADC min/max
auto limits = FPGA::GetADCLimits();
#define ADC_LIMIT 30000
// Compile info packet
packet.type = Protocol::PacketType::DeviceInfo;
packet.info.FPGA_configured = 1;
if(limits.P1min < -ADC_LIMIT || limits.P1max > ADC_LIMIT
|| limits.P2min < -ADC_LIMIT || limits.P2max > ADC_LIMIT
|| limits.Rmin < -ADC_LIMIT || limits.Rmax > ADC_LIMIT) {
packet.info.ADC_overload = true;
} else {
packet.info.ADC_overload = false;
}
packet.info.FW_major = FW_MAJOR;
packet.info.FW_minor = FW_MINOR;
packet.info.HW_Revision = HW_REVISION;
auto status = FPGA::GetStatus();
packet.info.LO1_locked = (status & (int) FPGA::Interrupt::LO1Unlock) ? 0 : 1;
packet.info.source_locked = (status & (int) FPGA::Interrupt::SourceUnlock) ? 0 : 1;
packet.info.extRefAvailable = 0;
packet.info.extRefInUse = 0;
packet.info.temperatures.LO1 = tempLO;
packet.info.temperatures.source = tempSource;
packet.info.temperatures.MCU = 0;
Communication::Send(packet);
FPGA::ResetADCLimits();
LOG_INFO("ADC limits: P1: %d/%d P2: %d/%d R: %d/%d",
limits.P1min, limits.P1max, limits.P2min, limits.P2max,
limits.Rmin, limits.Rmax);
// Start next sweep
FPGA::StartSweep();
}
}
if(notification & FLAG_STATUSRESULT) {
Protocol::PacketInfo p;
p.type = Protocol::PacketType::Status;
memset(&p.status, 0, sizeof(p.status));
uint16_t isr_flags = FPGA::GetStatus();
if (!(isr_flags & 0x0002)) {
p.status.source_locked = 1;
}
if (!(isr_flags & 0x0001)) {
p.status.LO_locked = 1;
}
auto limits = FPGA::GetADCLimits();
FPGA::ResetADCLimits(); FPGA::ResetADCLimits();
LOG_INFO("ADC limits: P1: %d/%d P2: %d/%d R: %d/%d", p.status.port1min = limits.P1min;
limits.P1min, limits.P1max, limits.P2min, limits.P2max, p.status.port1max = limits.P1max;
limits.Rmin, limits.Rmax); p.status.port2min = limits.P2min;
// Start next sweep p.status.port2max = limits.P2max;
p.status.refmin = limits.Rmin;
p.status.refmax = limits.Rmax;
p.status.port1real = (float) statusResult.P1I / manual.Samples;
p.status.port1imag = (float) statusResult.P1Q / manual.Samples;
p.status.port2real = (float) statusResult.P2I / manual.Samples;
p.status.port2imag = (float) statusResult.P2Q / manual.Samples;
p.status.refreal = (float) statusResult.RefI / manual.Samples;
p.status.refimag = (float) statusResult.RefQ / manual.Samples;
VNA::GetTemps(&p.status.temp_source, &p.status.temp_LO);
Communication::Send(p);
// Trigger next status update
FPGA::StartSweep(); FPGA::StartSweep();
} }
if(notification & FLAG_USB_PACKET) {
switch(packet.type) {
case Protocol::PacketType::SweepSettings:
LOG_INFO("New settings received");
settings = packet.settings;
VNA::ConfigureSweep(settings, VNACallback);
sweepActive = true;
lastNewPoint = HAL_GetTick();
break;
case Protocol::PacketType::ManualControl:
sweepActive = false;
manual = packet.manual;
VNA::ConfigureManual(manual, VNAStatusCallback);
break;
case Protocol::PacketType::ClearFlash:
FPGA::AbortSweep();
sweepActive = false;
LOG_DEBUG("Erasing FLASH in preparation for firmware update...");
if(flash.eraseChip()) {
LOG_DEBUG("...FLASH erased")
Protocol::PacketInfo p;
p.type = Protocol::PacketType::Ack;
Communication::Send(p);
} else {
LOG_ERR("Failed to erase FLASH");
}
break;
case Protocol::PacketType::FirmwarePacket:
LOG_INFO("Writing firmware packet at address %u", packet.firmware.address);
flash.write(packet.firmware.address, sizeof(packet.firmware.data), packet.firmware.data);
Protocol::PacketInfo p;
p.type = Protocol::PacketType::Ack;
Communication::Send(p);
break;
case Protocol::PacketType::PerformFirmwareUpdate: {
auto fw_info = Firmware::GetFlashContentInfo(&flash);
if(fw_info.valid) {
Protocol::PacketInfo p;
p.type = Protocol::PacketType::Ack;
Communication::Send(p);
// Some delay to allow communication to finish
vTaskDelay(1000);
Firmware::PerformUpdate(&flash);
// will never get here
}
}
break;
default:
// ignore all other packets
break;
}
}
} }
if (newSettings) {
LOG_INFO("New settings received");
newSettings = false;
VNA::ConfigureSweep(settings, VNACallback);
sweepActive = true;
lastNewPoint = HAL_GetTick();
}
if(sweepActive && HAL_GetTick() - lastNewPoint > 1000) { if(sweepActive && HAL_GetTick() - lastNewPoint > 1000) {
LOG_WARN("Timed out waiting for point, last received point was %d", result.pointNum); LOG_WARN("Timed out waiting for point, last received point was %d", result.pointNum);
LOG_WARN("FPGA status: 0x%04x", FPGA::GetStatus()); LOG_WARN("FPGA status: 0x%04x", FPGA::GetStatus());
@ -134,52 +228,5 @@ void App_Start() {
sweepActive = true; sweepActive = true;
lastNewPoint = HAL_GetTick(); lastNewPoint = HAL_GetTick();
} }
if (newManual) {
LOG_INFO("New manual control");
sweepActive = false;
newManual = false;
VNA::ConfigureManual(manual, VNAStatusCallback);
}
if (newStatus) {
newStatus = false;
Protocol::PacketInfo p;
p.type = Protocol::PacketType::Status;
memset(&p.status, 0, sizeof(p.status));
uint16_t isr_flags = FPGA::GetStatus();
if (!(isr_flags & 0x0002)) {
p.status.source_locked = 1;
}
if (!(isr_flags & 0x0001)) {
p.status.LO_locked = 1;
}
auto limits = FPGA::GetADCLimits();
FPGA::ResetADCLimits();
p.status.port1min = limits.P1min;
p.status.port1max = limits.P1max;
p.status.port2min = limits.P2min;
p.status.port2max = limits.P2max;
p.status.refmin = limits.Rmin;
p.status.refmax = limits.Rmax;
p.status.port1real = (float) statusResult.P1I / manual.Samples;
p.status.port1imag = (float) statusResult.P1Q / manual.Samples;
p.status.port2real = (float) statusResult.P2I / manual.Samples;
p.status.port2imag = (float) statusResult.P2Q / manual.Samples;
p.status.refreal = (float) statusResult.RefI / manual.Samples;
p.status.refimag = (float) statusResult.RefQ / manual.Samples;
VNA::GetTemps(&p.status.temp_source, &p.status.temp_LO);
Communication::Send(p);
// Trigger next status update
FPGA::StartSweep();
}
} }
} }
void App::NewSettings(Protocol::SweepSettings s) {
settings = s;
newSettings = true;
}
void App::SetManual(Protocol::ManualControl m) {
manual = m;
newManual = true;
}

View File

@ -3,13 +3,6 @@
#ifdef __cplusplus #ifdef __cplusplus
#include "Protocol.hpp" #include "Protocol.hpp"
namespace App {
void NewSettings(Protocol::SweepSettings s);
void SetManual(Protocol::ManualControl m);
}
extern "C" { extern "C" {
#endif #endif

View File

@ -9,10 +9,12 @@ static uint8_t inputBuffer[1024];
uint16_t inputCnt = 0; uint16_t inputCnt = 0;
static uint8_t outputBuffer[1024]; static uint8_t outputBuffer[1024];
//#include "usbd_def.h" static Communication::Callback callback = nullptr;
//#include "usbd_cdc_if.h"
void Communication::SetCallback(Callback cb) {
callback = cb;
}
//extern USBD_HandleTypeDef hUsbDeviceFS;
void Communication::Input(const uint8_t *buf, uint16_t len) { void Communication::Input(const uint8_t *buf, uint16_t len) {
if (inputCnt + len < sizeof(inputBuffer)) { if (inputCnt + len < sizeof(inputBuffer)) {
@ -33,13 +35,10 @@ void Communication::Input(const uint8_t *buf, uint16_t len) {
memmove(inputBuffer, &inputBuffer[handled_len], remaining); memmove(inputBuffer, &inputBuffer[handled_len], remaining);
inputCnt = remaining; inputCnt = remaining;
} }
switch(packet.type) { if(packet.type != Protocol::PacketType::None) {
case Protocol::PacketType::SweepSettings: if(callback) {
App::NewSettings(packet.settings); callback(packet);
break; }
case Protocol::PacketType::ManualControl:
App::SetManual(packet.manual);
break;
} }
} while (handled_len > 0); } while (handled_len > 0);
} }
@ -63,3 +62,4 @@ bool Communication::Send(Protocol::PacketInfo packet) {
void communication_usb_input(const uint8_t *buf, uint16_t len) { void communication_usb_input(const uint8_t *buf, uint16_t len) {
Communication::Input(buf, len); Communication::Input(buf, len);
} }

View File

@ -8,6 +8,9 @@
namespace Communication { namespace Communication {
using Callback = void(*)(Protocol::PacketInfo);
void SetCallback(Callback cb);
void Input(const uint8_t *buf, uint16_t len); void Input(const uint8_t *buf, uint16_t len);
bool Send(Protocol::PacketInfo packet); bool Send(Protocol::PacketInfo packet);

View File

@ -401,6 +401,8 @@ uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
info->firmware = DecodeFirmwarePacket(&data[4]); info->firmware = DecodeFirmwarePacket(&data[4]);
break; break;
case PacketType::Ack: case PacketType::Ack:
case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash:
// no payload, nothing to do // no payload, nothing to do
break; break;
case PacketType::None: case PacketType::None:
@ -432,6 +434,8 @@ uint16_t Protocol::EncodePacket(PacketInfo packet, uint8_t *dest, uint16_t dests
payload_size = EncodeFirmwarePacket(packet.firmware, &dest[4], destsize - 8); payload_size = EncodeFirmwarePacket(packet.firmware, &dest[4], destsize - 8);
break; break;
case PacketType::Ack: case PacketType::Ack:
case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash:
// no payload, nothing to do // no payload, nothing to do
break; break;
case PacketType::None: case PacketType::None:

View File

@ -91,14 +91,16 @@ using FirmwarePacket = struct _firmwarePacket {
}; };
enum class PacketType : uint8_t { enum class PacketType : uint8_t {
None, None = 0,
Datapoint, Datapoint = 1,
SweepSettings, SweepSettings = 2,
Status, Status = 3,
ManualControl, ManualControl = 4,
DeviceInfo, DeviceInfo = 5,
FirmwarePacket, FirmwarePacket = 6,
Ack, Ack = 7,
ClearFlash = 8,
PerformFirmwareUpdate = 9,
}; };
using PacketInfo = struct _packetinfo { using PacketInfo = struct _packetinfo {

View File

@ -21,7 +21,7 @@ public:
bool eraseChip(); bool eraseChip();
// Starts the reading process without actually reading any bytes // Starts the reading process without actually reading any bytes
void initiateRead(uint32_t address); void initiateRead(uint32_t address);
const SPI_HandleTypeDef* const& getSpi() const { const SPI_HandleTypeDef* const getSpi() const {
return spi; return spi;
} }