Fix spectrum: reliable serial key input + USB-C unplug freeze fix (issue #212)

This commit is contained in:
Armel FAUVEAU committed 2026-03-14 05:34:17 +01:00
1 parent fcf94e34ce
commit 9f0547f6af
10 files changed
+105 -54

No files matched your search

+3 -3
View File
@@ -139,7 +139,7 @@ static void AIRCOPY_clear()
crc[i] = 0; crc[i] = 0;
} }
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(true); SCREENSHOT_Update(true);
#endif #endif
} }
@@ -167,7 +167,7 @@ static inline void AIRCOPY_CheckComplete(void)
{ {
gAircopyState = AIRCOPY_COMPLETE; gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false); SCREENSHOT_Update(false);
#endif #endif
} }
} }
@@ -212,7 +212,7 @@ bool AIRCOPY_SendMessage(void)
if (CurrentSegmentIndex >= map->num_segments) { if (CurrentSegmentIndex >= map->num_segments) {
gAircopyState = AIRCOPY_COMPLETE; gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false); SCREENSHOT_Update(false);
#endif #endif
return 0; return 0;
} }
+1 -1
View File
@@ -1406,7 +1406,7 @@ void APP_TimeSlice10ms(void)
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
if (gUpdateDisplayCurrent || gUpdateStatusCurrent) { if (gUpdateDisplayCurrent || gUpdateStatusCurrent) {
getScreenShot(false); SCREENSHOT_Update(false);
} }
#endif #endif
+1 -1
View File
@@ -484,7 +484,7 @@ void APP_RunBreakout(void) {
// For screenshot // For screenshot
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false); SCREENSHOT_Update(false);
#endif #endif
} }
+7 -1
View File
@@ -1667,6 +1667,12 @@ static void UpdateListening()
static void Tick() static void Tick()
{ {
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
// Parse incoming packets on every tick so serial keys are never missed,
// regardless of whether the screen needs redrawing.
SCREENSHOT_ParseInput();
#endif
if (gNextTimeslice) if (gNextTimeslice)
{ {
gNextTimeslice = false; gNextTimeslice = false;
@@ -1737,7 +1743,7 @@ static void Tick()
Render(); Render();
// For screenshot // For screenshot
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false); SCREENSHOT_Update(false);
#endif #endif
redrawScreen = false; redrawScreen = false;
} }
+1 -1
View File
@@ -165,7 +165,7 @@ void ST7565_DrawLine(const unsigned int Column, const unsigned int Line, const u
{ {
ST7565_BlitScreen(line + 1); ST7565_BlitScreen(line + 1);
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(true); // Force immediate capture SCREENSHOT_Update(true); // Force immediate capture
#endif #endif
} }
+58 -31
View File
@@ -155,43 +155,70 @@ void UART_LogSend(const void *pBuffer, uint32_t Size)
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
bool UART_IsCableConnected(void) { bool UART_IsCableConnected(void) {
typedef enum {
STATE_IDLE = 0,
STATE_KA_1,
STATE_KA_2,
STATE_KA_3,
STATE_KEY_1,
STATE_KEY_2,
STATE_KEY_3,
STATE_KEY_3L,
} ParseState_t;
static uint8_t read_ptr = 0;
static ParseState_t state = STATE_IDLE;
bool connected = false; bool connected = false;
for (size_t i = 0; i < sizeof(UART_DMA_Buffer); i++) { // DMA write position: NbData counts DOWN from 256
uint8_t b = UART_DMA_Buffer[i]; uint8_t write_ptr = (uint8_t)(sizeof(UART_DMA_Buffer) -
LL_DMA_GetDataLength(DMA1, DMA_CHANNEL));
if (b == 0x55) { uint8_t processed = 0;
// Keepalive byte — viewer is alive while (read_ptr != write_ptr && processed < sizeof(UART_DMA_Buffer))
UART_DMA_Buffer[i] = 0x00;
connected = true;
}
else if (b == 0xAA) {
// Possible start of a key packet: 0xAA 0x55 <type> <keycode>
// type 0x03 = short press, 0x04 = long press
size_t i1 = (i + 1) % sizeof(UART_DMA_Buffer);
size_t i2 = (i + 2) % sizeof(UART_DMA_Buffer);
size_t i3 = (i + 3) % sizeof(UART_DMA_Buffer);
if (UART_DMA_Buffer[i1] == 0x55 &&
(UART_DMA_Buffer[i2] == UART_TYPE_KEY ||
UART_DMA_Buffer[i2] == UART_TYPE_KEY_LONG))
{ {
uint8_t type = UART_DMA_Buffer[i2]; uint8_t b = UART_DMA_Buffer[read_ptr++];
uint8_t keyCode = UART_DMA_Buffer[i3]; // read_ptr wraps naturally at 256 since it's uint8_t
processed++;
// Consume all 4 bytes
UART_DMA_Buffer[i] = 0x00;
UART_DMA_Buffer[i1] = 0x00;
UART_DMA_Buffer[i2] = 0x00;
UART_DMA_Buffer[i3] = 0x00;
if (type == UART_TYPE_KEY_LONG)
KEYBOARD_InjectKeyLong(keyCode);
else
KEYBOARD_InjectKey(keyCode);
switch (state)
{
case STATE_IDLE:
if (b == 0x55) state = STATE_KA_1;
else if (b == 0xAA) state = STATE_KEY_1;
break;
case STATE_KA_1:
state = (b == 0xAA) ? STATE_KA_2 : STATE_IDLE;
break;
case STATE_KA_2:
state = (b == 0x00) ? STATE_KA_3 : STATE_IDLE;
break;
case STATE_KA_3:
if (b == 0x00) connected = true;
state = STATE_IDLE;
break;
case STATE_KEY_1:
state = (b == 0x55) ? STATE_KEY_2 : STATE_IDLE;
break;
case STATE_KEY_2:
if (b == UART_TYPE_KEY) state = STATE_KEY_3;
else if (b == UART_TYPE_KEY_LONG) state = STATE_KEY_3L;
else state = STATE_IDLE;
break;
case STATE_KEY_3:
KEYBOARD_InjectKey(b);
connected = true; connected = true;
} state = STATE_IDLE;
break;
case STATE_KEY_3L:
KEYBOARD_InjectKeyLong(b);
connected = true;
state = STATE_IDLE;
break;
default:
state = STATE_IDLE;
break;
} }
} }
+25 -13
View File
@@ -21,7 +21,26 @@
#include "driver/vcp.h" #include "driver/vcp.h"
#include "driver/keyboard.h" #include "driver/keyboard.h"
static void Screenshot_Send(const uint8_t *buf, uint16_t len) // SRAM optimization: minimize static allocations
// - previousFrame: 1024 bytes (REQUIRED - need to compare for delta)
// - No currentFrame or deltaFrame static buffers
static uint8_t previousFrame[1024] = {0};
static uint8_t forcedBlock = 0;
static uint8_t keepAlive = 10;
void SCREENSHOT_ParseInput(void)
{
if (UART_IsCableConnected()) {
keepAlive = 10;
gUSB_ScreenshotEnabled = false;
}
if (VCP_ScreenshotPing()) {
keepAlive = 10;
gUSB_ScreenshotEnabled = true;
}
}
static void SCREENSHOT_Send(const uint8_t *buf, uint16_t len)
{ {
if (gUSB_ScreenshotEnabled) { if (gUSB_ScreenshotEnabled) {
cdc_acm_data_send_with_dtr(buf, len); cdc_acm_data_send_with_dtr(buf, len);
@@ -30,14 +49,7 @@ static void Screenshot_Send(const uint8_t *buf, uint16_t len)
} }
} }
// SRAM optimization: minimize static allocations void SCREENSHOT_Update(bool force)
// - previousFrame: 1024 bytes (REQUIRED - need to compare for delta)
// - No currentFrame or deltaFrame static buffers
static uint8_t previousFrame[1024] = {0};
static uint8_t forcedBlock = 0;
static uint8_t keepAlive = 10;
void getScreenShot(bool force)
{ {
// Build frame in a temporary stack buffer // Build frame in a temporary stack buffer
// This is 1024 bytes but it's temporary and gets freed after the function // This is 1024 bytes but it's temporary and gets freed after the function
@@ -135,7 +147,7 @@ void getScreenShot(bool force)
// New format: sends 0xFF before header // New format: sends 0xFF before header
// Old format: doesn't exist, so viewers can differentiate // Old format: doesn't exist, so viewers can differentiate
uint8_t versionMarker = 0xFF; uint8_t versionMarker = 0xFF;
Screenshot_Send(&versionMarker, 1); SCREENSHOT_Send(&versionMarker, 1);
// ==== Send header ==== // ==== Send header ====
uint8_t header[5] = { uint8_t header[5] = {
@@ -144,7 +156,7 @@ void getScreenShot(bool force)
(uint8_t)(deltaLen & 0xFF) (uint8_t)(deltaLen & 0xFF)
}; };
Screenshot_Send(header, 5); SCREENSHOT_Send(header, 5);
// ==== SECOND PASS: Send only changed chunks ==== // ==== SECOND PASS: Send only changed chunks ====
uint8_t chunk[9]; uint8_t chunk[9];
@@ -157,12 +169,12 @@ void getScreenShot(bool force)
chunk[0] = chunkIdx; chunk[0] = chunkIdx;
memcpy(&chunk[1], cur, 8); memcpy(&chunk[1], cur, 8);
Screenshot_Send(chunk, 9); SCREENSHOT_Send(chunk, 9);
// Update previousFrame for next comparison // Update previousFrame for next comparison
memcpy(prev, cur, 8); memcpy(prev, cur, 8);
} }
uint8_t end = 0x0A; uint8_t end = 0x0A;
Screenshot_Send(&end, 1); SCREENSHOT_Send(&end, 1);
} }
+2 -1
View File
@@ -17,6 +17,7 @@
#ifndef SCREENSHOT_H #ifndef SCREENSHOT_H
#define SCREENSHOT_H #define SCREENSHOT_H
void getScreenShot(bool force); void SCREENSHOT_Update(bool force);
void SCREENSHOT_ParseInput(void);
#endif #endif
+1 -1
View File
@@ -222,7 +222,7 @@ void UI_DisplayWelcome(void)
ST7565_BlitFullScreen(); ST7565_BlitFullScreen();
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(true); SCREENSHOT_Update(true);
#endif #endif
} }
} }
+6 -1
View File
@@ -190,8 +190,13 @@ void cdc_acm_data_send_with_dtr(const uint8_t *buf, uint32_t size)
{ {
ep_tx_busy_flag = true; ep_tx_busy_flag = true;
usbd_ep_start_write(CDC_IN_EP, buf, size); usbd_ep_start_write(CDC_IN_EP, buf, size);
while (ep_tx_busy_flag) uint32_t timeout = 100000;
while (ep_tx_busy_flag && --timeout)
; ;
if (!timeout) {
ep_tx_busy_flag = false;
dtr_enable = 0; // Consider USB disconnected
}
} }
} }