mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 03:15:37 +00:00
Make Aircopy transfers reliable with ACK/RESEND recovery and leaner FSK handling
This commit is contained in:
1 parent
ce6bf08b23
commit
285f543d46
13 files changed
+299
-366
No files matched your search
+220
-198
@@ -21,6 +21,7 @@
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/crc.h"
|
||||
#include "driver/eeprom.h"
|
||||
#include "driver/system.h"
|
||||
#include "frequencies.h"
|
||||
#include "misc.h"
|
||||
#include "radio.h"
|
||||
@@ -29,6 +30,7 @@
|
||||
#include "ui/ui.h"
|
||||
#include "settings.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
#include "k5viewer.h"
|
||||
@@ -43,140 +45,73 @@ bool gAirCopyIsSendMode;
|
||||
|
||||
uint16_t g_FSK_Buffer[36];
|
||||
|
||||
// ============================================================================
|
||||
// Transfer Maps Definition
|
||||
// ============================================================================
|
||||
// Stop-and-wait protocol. Every frame keeps the original 64-byte payload:
|
||||
// DATA is acknowledged only after storage, ACK confirms that offset, and
|
||||
// RESEND requests the same offset immediately instead of waiting for timeout.
|
||||
#define AIRCOPY_PACKET_DATA 0xABCDu
|
||||
#define AIRCOPY_PACKET_ACK 0xABCEu
|
||||
#define AIRCOPY_PACKET_RESEND 0xABCFu
|
||||
#define AIRCOPY_PACKET_END 0xDCBAu
|
||||
#define AIRCOPY_ACK_TIMEOUT_10MS 400u
|
||||
#define AIRCOPY_RX_TIMEOUT_10MS 2000u
|
||||
#define AIRCOPY_RX_LINGER_10MS 500u
|
||||
#define AIRCOPY_MAX_RETRIES 3u
|
||||
|
||||
#define AIRCOPY_BANK_SEGMENTS(bank) \
|
||||
{ \
|
||||
{ 0x0000 + (bank)*0x0800, 0x0000 + (bank)*0x0800 + 0x0800, AIRCOPY_WRITE_STRUCT }, \
|
||||
{ 0x4000 + (bank)*0x0800, 0x4000 + (bank)*0x0800 + 0x0800, AIRCOPY_WRITE_STRUCT }, \
|
||||
{ 0x8000 + (bank)*0x0100, 0x8000 + (bank)*0x0100 + 0x0100, AIRCOPY_WRITE_BYTES }, \
|
||||
}
|
||||
static uint16_t AircopyCountdown;
|
||||
static uint8_t AircopyRetries;
|
||||
|
||||
#define AIRCOPY_STD_MAP(seg_array) \
|
||||
{ \
|
||||
.segments = seg_array, \
|
||||
.num_segments = 3, \
|
||||
.total_blocks = 68 \
|
||||
}
|
||||
|
||||
// total_blocks = 68 (blocs of 64 bytes) because (16 bytes + 16 bytes + 2 bytes) * 128 = 4352 / 64 = 68
|
||||
|
||||
#define DECLARE_AIRCOPY_BANK(n) \
|
||||
static const AIRCOPY_Segment_t AIRCOPY_Segments_Bank##n[] = \
|
||||
AIRCOPY_BANK_SEGMENTS(n); \
|
||||
\
|
||||
static const AIRCOPY_TransferMap_t AIRCOPY_Map_Bank##n = \
|
||||
AIRCOPY_STD_MAP(AIRCOPY_Segments_Bank##n);
|
||||
|
||||
DECLARE_AIRCOPY_BANK(0)
|
||||
DECLARE_AIRCOPY_BANK(1)
|
||||
#if AIRCOPY_NUM_BANKS >= 4 // if 512 MR CHANNEL
|
||||
DECLARE_AIRCOPY_BANK(2)
|
||||
DECLARE_AIRCOPY_BANK(3)
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 6 // if 758 MR CHANNEL
|
||||
DECLARE_AIRCOPY_BANK(4)
|
||||
DECLARE_AIRCOPY_BANK(5)
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 8 // if 1024 MR CHANNEL
|
||||
DECLARE_AIRCOPY_BANK(6)
|
||||
DECLARE_AIRCOPY_BANK(7)
|
||||
#endif
|
||||
|
||||
// For settings only
|
||||
|
||||
static const AIRCOPY_Segment_t AIRCOPY_Segments_Settings[] = {
|
||||
// Ends are rounded to full Aircopy blocks. The extra bytes are unmapped
|
||||
// EEPROM-compat holes (0x90E0..0x90E6 is the complete Fox Hunt tail).
|
||||
{ 0xA000, 0xA180, AIRCOPY_WRITE_BYTES },
|
||||
{ 0x880E, 0x888E, AIRCOPY_WRITE_BYTES },
|
||||
{ 0x9000, 0x9100, AIRCOPY_WRITE_BYTES },
|
||||
};
|
||||
|
||||
// total_blocks = 0x180/64 + 0x80/64 + 0x100/64 = 6 + 2 + 4 = 12
|
||||
static const AIRCOPY_TransferMap_t AIRCOPY_Map_Settings = {
|
||||
.segments = AIRCOPY_Segments_Settings,
|
||||
.num_segments = 3,
|
||||
.total_blocks = 12
|
||||
};
|
||||
|
||||
// Finally
|
||||
|
||||
static const AIRCOPY_TransferMap_t *AIRCOPY_AvailableMaps[] = {
|
||||
&AIRCOPY_Map_Bank0,
|
||||
&AIRCOPY_Map_Bank1,
|
||||
#if AIRCOPY_NUM_BANKS >= 4 // if 512 MR CHANNEL
|
||||
&AIRCOPY_Map_Bank2,
|
||||
&AIRCOPY_Map_Bank3,
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 6 // if 758 MR CHANNEL
|
||||
&AIRCOPY_Map_Bank4,
|
||||
&AIRCOPY_Map_Bank5,
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 8 // if 1024 MR CHANNEL
|
||||
&AIRCOPY_Map_Bank6,
|
||||
&AIRCOPY_Map_Bank7,
|
||||
#endif
|
||||
&AIRCOPY_Map_Settings,
|
||||
};
|
||||
|
||||
#define AIRCOPY_NUM_MAPS (sizeof(AIRCOPY_AvailableMaps) / sizeof(AIRCOPY_AvailableMaps[0]))
|
||||
#define AIRCOPY_NUM_MAPS (AIRCOPY_NUM_BANKS + 1u)
|
||||
#define AIRCOPY_BANK_BLOCKS 68u
|
||||
#define AIRCOPY_SETTINGS_BLOCKS 12u
|
||||
|
||||
// ============================================================================
|
||||
// Helper Functions
|
||||
// ============================================================================
|
||||
|
||||
const AIRCOPY_TransferMap_t* AIRCOPY_GetCurrentMap(void)
|
||||
uint8_t AIRCOPY_GetTotalBlocks(void)
|
||||
{
|
||||
if (gAircopyCurrentMapIndex >= AIRCOPY_NUM_MAPS) {
|
||||
gAircopyCurrentMapIndex = 0;
|
||||
return gAircopyCurrentMapIndex == AIRCOPY_NUM_BANKS
|
||||
? AIRCOPY_SETTINGS_BLOCKS
|
||||
: AIRCOPY_BANK_BLOCKS;
|
||||
}
|
||||
|
||||
static uint16_t AIRCOPY_GetBlockOffset(uint16_t block)
|
||||
{
|
||||
if (gAircopyCurrentMapIndex == AIRCOPY_NUM_BANKS)
|
||||
{
|
||||
// Settings: 6 blocks at 0xA000, 2 at 0x880E and 4 at 0x9000.
|
||||
if (block < 6u)
|
||||
return 0xA000u + block * AIRCOPY_BLOCK_SIZE;
|
||||
if (block < 8u)
|
||||
return 0x880Eu + (block - 6u) * AIRCOPY_BLOCK_SIZE;
|
||||
return 0x9000u + (block - 8u) * AIRCOPY_BLOCK_SIZE;
|
||||
}
|
||||
return AIRCOPY_AvailableMaps[gAircopyCurrentMapIndex];
|
||||
|
||||
// A bank contains 32 frequency, 32 name and 4 attribute blocks.
|
||||
const uint16_t channelOffset = gAircopyCurrentMapIndex * 0x0800u;
|
||||
if (block < 32u)
|
||||
return channelOffset + block * AIRCOPY_BLOCK_SIZE;
|
||||
if (block < 64u)
|
||||
return 0x4000u + channelOffset + (block - 32u) * AIRCOPY_BLOCK_SIZE;
|
||||
return 0x8000u + gAircopyCurrentMapIndex * 0x0100u
|
||||
+ (block - 64u) * AIRCOPY_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
static void AIRCOPY_clear()
|
||||
{
|
||||
for (uint8_t i = 0; i < 15; i++)
|
||||
{
|
||||
crc[i] = 0;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
K5VIEWER_Update(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline const AIRCOPY_Segment_t *AIRCOPY_FindSegmentForOffset(uint16_t off)
|
||||
static void AIRCOPY_Finish(AIRCOPY_State_t state)
|
||||
{
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
|
||||
for (uint16_t i = 0; i < map->num_segments; i++)
|
||||
{
|
||||
const AIRCOPY_Segment_t *seg = &map->segments[i];
|
||||
|
||||
if (off >= seg->start_offset && off < seg->end_offset &&
|
||||
((off - seg->start_offset) & (AIRCOPY_BLOCK_SIZE - 1u)) == 0u)
|
||||
return seg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void AIRCOPY_CheckComplete(uint16_t *num)
|
||||
{
|
||||
*num = *num + 1;
|
||||
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
uint16_t done = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
if (done >= map->total_blocks)
|
||||
{
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
AircopyCountdown = 0;
|
||||
gAircopyState = state;
|
||||
gUpdateDisplay = true;
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
K5VIEWER_Update(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void AIRCOPY_Obfuscate(unsigned int count)
|
||||
@@ -186,70 +121,97 @@ void AIRCOPY_Obfuscate(unsigned int count)
|
||||
}
|
||||
}
|
||||
|
||||
static void AIRCOPY_TransmitBuffer(void)
|
||||
{
|
||||
// Both sides need time to leave TX and re-arm FSK RX before the reply.
|
||||
SYSTEM_DelayMs(50);
|
||||
RADIO_SetTxParameters();
|
||||
BK4819_SendFSKData(g_FSK_Buffer);
|
||||
BK4819_SetupPowerAmplifier(0, 0);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
}
|
||||
|
||||
static void AIRCOPY_FinalizeAndSend(void)
|
||||
{
|
||||
g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[0],
|
||||
4 + AIRCOPY_BLOCK_SIZE);
|
||||
g_FSK_Buffer[35] = AIRCOPY_PACKET_END;
|
||||
AIRCOPY_Obfuscate(34);
|
||||
AIRCOPY_TransmitBuffer();
|
||||
gFSKWriteIndex = 0;
|
||||
BK4819_PrepareFSKReceive();
|
||||
}
|
||||
|
||||
static void AIRCOPY_SendControl(uint16_t type, uint16_t offset)
|
||||
{
|
||||
g_FSK_Buffer[0] = type;
|
||||
g_FSK_Buffer[1] = offset;
|
||||
memset(&g_FSK_Buffer[2], 0, AIRCOPY_BLOCK_SIZE);
|
||||
AIRCOPY_FinalizeAndSend();
|
||||
}
|
||||
|
||||
static void AIRCOPY_RequestResend(void)
|
||||
{
|
||||
gErrorsDuringAirCopy++;
|
||||
gUpdateDisplay = true;
|
||||
AircopyCountdown = AIRCOPY_RX_TIMEOUT_10MS;
|
||||
AIRCOPY_SendControl(AIRCOPY_PACKET_RESEND,
|
||||
AIRCOPY_GetBlockOffset(gAirCopyBlockNumber));
|
||||
}
|
||||
|
||||
static bool AIRCOPY_Retry(void)
|
||||
{
|
||||
if (AircopyRetries >= AIRCOPY_MAX_RETRIES)
|
||||
{
|
||||
AIRCOPY_Finish(AIRCOPY_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
AircopyRetries++;
|
||||
gErrorsDuringAirCopy++;
|
||||
gUpdateDisplay = true;
|
||||
AircopyCountdown = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Send/Receive Functions
|
||||
// ============================================================================
|
||||
|
||||
bool AIRCOPY_SendMessage(void)
|
||||
{
|
||||
static uint8_t gAircopySendCountdown = 1;
|
||||
static uint16_t CurrentOffset = 0;
|
||||
static uint16_t CurrentSegmentIndex = 0;
|
||||
|
||||
if (gAircopyState != AIRCOPY_TRANSFER) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (--gAircopySendCountdown) {
|
||||
if (!gAirCopyIsSendMode)
|
||||
{
|
||||
if (AircopyCountdown != 0 && --AircopyCountdown == 0)
|
||||
{
|
||||
AIRCOPY_Finish(gAirCopyBlockNumber >= AIRCOPY_GetTotalBlocks()
|
||||
? AIRCOPY_COMPLETE
|
||||
: AIRCOPY_FAILED);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
|
||||
// Initialize on first call
|
||||
if (gAirCopyBlockNumber == 0) {
|
||||
CurrentSegmentIndex = 0;
|
||||
CurrentOffset = map->segments[0].start_offset;
|
||||
}
|
||||
|
||||
// Advance to next segment if current is done
|
||||
while (CurrentSegmentIndex < map->num_segments &&
|
||||
CurrentOffset >= map->segments[CurrentSegmentIndex].end_offset)
|
||||
if (AircopyCountdown != 0)
|
||||
{
|
||||
CurrentSegmentIndex++;
|
||||
if (CurrentSegmentIndex < map->num_segments) {
|
||||
CurrentOffset = map->segments[CurrentSegmentIndex].start_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if transfer is complete
|
||||
if (CurrentSegmentIndex >= map->num_segments) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
K5VIEWER_Update(false);
|
||||
#endif
|
||||
if (--AircopyCountdown != 0)
|
||||
return 1;
|
||||
if (!AIRCOPY_Retry())
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Send data from current offset
|
||||
g_FSK_Buffer[1] = CurrentOffset;
|
||||
EEPROM_ReadBuffer(CurrentOffset, &g_FSK_Buffer[2], 64);
|
||||
const uint16_t currentOffset = AIRCOPY_GetBlockOffset(gAirCopyBlockNumber);
|
||||
g_FSK_Buffer[0] = AIRCOPY_PACKET_DATA;
|
||||
g_FSK_Buffer[1] = currentOffset;
|
||||
EEPROM_ReadBuffer(currentOffset, &g_FSK_Buffer[2], AIRCOPY_BLOCK_SIZE);
|
||||
AIRCOPY_FinalizeAndSend();
|
||||
AircopyCountdown = AIRCOPY_ACK_TIMEOUT_10MS;
|
||||
|
||||
g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
|
||||
AIRCOPY_Obfuscate(34);
|
||||
|
||||
RADIO_SetTxParameters();
|
||||
|
||||
BK4819_SendFSKData(g_FSK_Buffer);
|
||||
BK4819_SetupPowerAmplifier(0, 0);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
|
||||
CurrentOffset += 64;
|
||||
gAirCopyBlockNumber++;
|
||||
gAircopySendCountdown = 30;
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void AIRCOPY_StorePacket(void)
|
||||
@@ -259,53 +221,111 @@ void AIRCOPY_StorePacket(void)
|
||||
}
|
||||
|
||||
gFSKWriteIndex = 0;
|
||||
gUpdateDisplay = true;
|
||||
uint16_t Status = BK4819_ReadRegister(BK4819_REG_0B);
|
||||
BK4819_PrepareFSKReceive();
|
||||
const uint16_t status = BK4819_ReadRegister(BK4819_REG_0B);
|
||||
const uint16_t type = g_FSK_Buffer[0];
|
||||
bool valid = (status & 0x0010u) == 0 &&
|
||||
(type == AIRCOPY_PACKET_DATA ||
|
||||
type == AIRCOPY_PACKET_ACK ||
|
||||
type == AIRCOPY_PACKET_RESEND) &&
|
||||
g_FSK_Buffer[35] == AIRCOPY_PACKET_END;
|
||||
|
||||
if ((Status & 0x0010U) != 0 || g_FSK_Buffer[0] != 0xABCD || g_FSK_Buffer[35] != 0xDCBA) {
|
||||
BK4819_ResetFSK(); // <- important
|
||||
BK4819_PrepareFSKReceive(); // <- re-arm proprement
|
||||
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
AIRCOPY_Obfuscate(34);
|
||||
|
||||
uint16_t Crc = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
if (g_FSK_Buffer[34] != Crc) {
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t Offset = g_FSK_Buffer[1];
|
||||
|
||||
const AIRCOPY_Segment_t *seg = AIRCOPY_FindSegmentForOffset(Offset);
|
||||
|
||||
if (seg == NULL) {
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t *pData = (const uint8_t *)&g_FSK_Buffer[2];
|
||||
|
||||
for (unsigned int i = 0; i < 8; i++)
|
||||
if (valid)
|
||||
{
|
||||
EEPROM_WriteBuffer(Offset + (i * 8), pData + (i * 8));
|
||||
AIRCOPY_Obfuscate(34);
|
||||
valid = g_FSK_Buffer[34] ==
|
||||
CRC_Calculate(&g_FSK_Buffer[0], 4 + AIRCOPY_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
AIRCOPY_CheckComplete(&gAirCopyBlockNumber);
|
||||
if (gAirCopyIsSendMode)
|
||||
{
|
||||
if (!valid)
|
||||
{
|
||||
BK4819_PrepareFSKReceive();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t offset = g_FSK_Buffer[1];
|
||||
const uint16_t currentOffset = AIRCOPY_GetBlockOffset(gAirCopyBlockNumber);
|
||||
if (type == AIRCOPY_PACKET_ACK && offset == currentOffset)
|
||||
{
|
||||
AircopyCountdown = 0;
|
||||
AircopyRetries = 0;
|
||||
gAirCopyBlockNumber++;
|
||||
gUpdateDisplay = true;
|
||||
if (gAirCopyBlockNumber >= AIRCOPY_GetTotalBlocks())
|
||||
AIRCOPY_Finish(AIRCOPY_COMPLETE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == AIRCOPY_PACKET_RESEND && offset == currentOffset)
|
||||
{
|
||||
(void)AIRCOPY_Retry();
|
||||
return;
|
||||
}
|
||||
|
||||
BK4819_PrepareFSKReceive();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
if (type == AIRCOPY_PACKET_DATA)
|
||||
AIRCOPY_RequestResend();
|
||||
else
|
||||
BK4819_PrepareFSKReceive();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != AIRCOPY_PACKET_DATA)
|
||||
{
|
||||
BK4819_PrepareFSKReceive();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t offset = g_FSK_Buffer[1];
|
||||
if (gAirCopyBlockNumber != 0u &&
|
||||
offset == AIRCOPY_GetBlockOffset(gAirCopyBlockNumber - 1u))
|
||||
{
|
||||
AircopyCountdown = gAirCopyBlockNumber >= AIRCOPY_GetTotalBlocks()
|
||||
? AIRCOPY_RX_LINGER_10MS
|
||||
: AIRCOPY_RX_TIMEOUT_10MS;
|
||||
AIRCOPY_SendControl(AIRCOPY_PACKET_ACK, offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset != AIRCOPY_GetBlockOffset(gAirCopyBlockNumber))
|
||||
{
|
||||
AIRCOPY_RequestResend();
|
||||
return;
|
||||
}
|
||||
|
||||
EEPROM_WriteBuffer(offset, &g_FSK_Buffer[2], AIRCOPY_BLOCK_SIZE);
|
||||
// All pending RX errors concerned this stop-and-wait block.
|
||||
gErrorsDuringAirCopy = 0;
|
||||
gAirCopyBlockNumber++;
|
||||
gUpdateDisplay = true;
|
||||
|
||||
AircopyCountdown = gAirCopyBlockNumber < AIRCOPY_GetTotalBlocks()
|
||||
? AIRCOPY_RX_TIMEOUT_10MS
|
||||
: AIRCOPY_RX_LINGER_10MS;
|
||||
|
||||
AIRCOPY_SendControl(AIRCOPY_PACKET_ACK, offset);
|
||||
}
|
||||
|
||||
static void AIRCOPY_InitTransfer(bool isSendMode)
|
||||
{
|
||||
gAircopyStep = 1;
|
||||
if (gAircopyCurrentMapIndex >= AIRCOPY_NUM_MAPS)
|
||||
gAircopyCurrentMapIndex = 0;
|
||||
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gErrorsDuringAirCopy = 0;
|
||||
gInputBoxIndex = 0;
|
||||
gAirCopyIsSendMode = isSendMode;
|
||||
|
||||
AircopyCountdown = isSendMode ? 0 : AIRCOPY_RX_TIMEOUT_10MS;
|
||||
AircopyRetries = 0;
|
||||
|
||||
AIRCOPY_clear();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
@@ -359,8 +379,6 @@ static void AIRCOPY_Key_EXIT()
|
||||
{
|
||||
if (gInputBoxIndex == 0) {
|
||||
AIRCOPY_InitTransfer(0); // Mode: Receive
|
||||
gErrorsDuringAirCopy = lErrorsDuringAirCopy = 0;
|
||||
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
} else {
|
||||
@@ -371,10 +389,6 @@ static void AIRCOPY_Key_EXIT()
|
||||
static void AIRCOPY_Key_MENU()
|
||||
{
|
||||
AIRCOPY_InitTransfer(1); // Mode: Send
|
||||
|
||||
g_FSK_Buffer[0] = 0xABCD;
|
||||
g_FSK_Buffer[1] = 0;
|
||||
g_FSK_Buffer[35] = 0xDCBA;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_UP_DOWN(int8_t Direction)
|
||||
@@ -400,6 +414,14 @@ void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gAircopyState == AIRCOPY_COMPLETE || gAircopyState == AIRCOPY_FAILED)
|
||||
{
|
||||
gAircopyState = AIRCOPY_READY;
|
||||
gUpdateDisplay = true;
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Key != KEY_PTT) {
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
+3
-48
@@ -28,53 +28,8 @@
|
||||
#define AIRCOPY_BLOCK_SIZE 0x0040u // 64 bytes per AirCopy block
|
||||
#define AIRCOPY_CHANNELS_PER_BANK 128
|
||||
#define AIRCOPY_NUM_BANKS MR_CHANNELS_MAX / AIRCOPY_CHANNELS_PER_BANK
|
||||
#define AIRCOPY_CHANNEL_SIZE 16 // bytes per channel (freq/name)
|
||||
#define AIRCOPY_BANK_SIZE_BYTES 0x1080u // 0x800 (Freq) + 0x800 (Name) + 0x80 (Attr)
|
||||
#define AIRCOPY_BAR_WIDTH 120 // Visible width of the progress gauge
|
||||
|
||||
// ============================================================================
|
||||
// Segment write mode
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
* Defines how a segment must be written to EEPROM.
|
||||
*
|
||||
* - STRUCT: structured data (frequencies, names)
|
||||
* - BYTES : raw byte stream (attributes, settings, etc.)
|
||||
*/
|
||||
typedef enum {
|
||||
AIRCOPY_WRITE_STRUCT = 0,
|
||||
AIRCOPY_WRITE_BYTES = 1,
|
||||
} AIRCOPY_WriteMode_t;
|
||||
|
||||
// ============================================================================
|
||||
// Transfer segment structure
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
* Describes a contiguous EEPROM region involved in AirCopy.
|
||||
* The write_mode defines how the RX side must write the data.
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t start_offset;
|
||||
uint16_t end_offset;
|
||||
AIRCOPY_WriteMode_t write_mode;
|
||||
} AIRCOPY_Segment_t;
|
||||
|
||||
// ============================================================================
|
||||
// Transfer map structure
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
* A transfer map is a collection of segments describing
|
||||
* one complete AirCopy operation (bank, settings, etc.).
|
||||
*/
|
||||
typedef struct {
|
||||
const AIRCOPY_Segment_t *segments;
|
||||
uint16_t num_segments;
|
||||
uint16_t total_blocks;
|
||||
} AIRCOPY_TransferMap_t;
|
||||
|
||||
// ============================================================================
|
||||
// AirCopy state
|
||||
// ============================================================================
|
||||
@@ -82,7 +37,8 @@ typedef struct {
|
||||
typedef enum {
|
||||
AIRCOPY_READY = 0,
|
||||
AIRCOPY_TRANSFER,
|
||||
AIRCOPY_COMPLETE
|
||||
AIRCOPY_COMPLETE,
|
||||
AIRCOPY_FAILED
|
||||
} AIRCOPY_State_t;
|
||||
|
||||
// ============================================================================
|
||||
@@ -103,8 +59,7 @@ extern uint16_t g_FSK_Buffer[36];
|
||||
bool AIRCOPY_SendMessage(void);
|
||||
void AIRCOPY_StorePacket(void);
|
||||
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
const AIRCOPY_TransferMap_t* AIRCOPY_GetCurrentMap(void);
|
||||
uint8_t AIRCOPY_GetTotalBlocks(void);
|
||||
|
||||
// XOR-obfuscate `count` words of g_FSK_Buffer starting at index 1.
|
||||
// Self-inverse: applying twice restores the original buffer.
|
||||
|
||||
+27
-19
@@ -1007,36 +1007,44 @@ static void CheckRadioInterrupts(void)
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (interrupts.fskFifoAlmostFull &&
|
||||
gScreenToDisplay == DISPLAY_AIRCOPY &&
|
||||
gAircopyState == AIRCOPY_TRANSFER &&
|
||||
gAirCopyIsSendMode == 0)
|
||||
#if defined(ENABLE_AIRCOPY) || defined(ENABLE_FEAT_F4HWN_BEAM)
|
||||
if (interrupts.fskFifoAlmostFull || interrupts.fskRxFinied)
|
||||
{
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
g_FSK_Buffer[gFSKWriteIndex++] = BK4819_ReadRegister(BK4819_REG_5F);
|
||||
}
|
||||
|
||||
AIRCOPY_StorePacket();
|
||||
}
|
||||
#endif
|
||||
uint8_t fskTarget = 0;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
if ((interrupts.fskFifoAlmostFull || interrupts.fskRxFinied) &&
|
||||
gBeamActive &&
|
||||
if (gBeamActive &&
|
||||
gBeamMode == BEAM_MODE_RX &&
|
||||
(gBeamStatus == BEAM_STATUS_RX_WAIT || gBeamStatus == BEAM_STATUS_ERROR))
|
||||
fskTarget = 2;
|
||||
#endif
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
// Aircopy wins if stale state ever makes both receivers eligible.
|
||||
if (gScreenToDisplay == DISPLAY_AIRCOPY &&
|
||||
gAircopyState == AIRCOPY_TRANSFER)
|
||||
fskTarget = 1;
|
||||
#endif
|
||||
|
||||
if (fskTarget != 0)
|
||||
{
|
||||
const unsigned int wordsToRead = interrupts.fskRxFinied ? (36 - gFSKWriteIndex) : 4;
|
||||
const unsigned int wordsToRead = interrupts.fskRxFinied
|
||||
? (gFSKWriteIndex < 36 ? 36u - gFSKWriteIndex : 0u)
|
||||
: 4u;
|
||||
for (unsigned int i = 0; i < wordsToRead; i++) {
|
||||
const uint16_t word = BK4819_ReadRegister(BK4819_REG_5F);
|
||||
if (gFSKWriteIndex < 36)
|
||||
g_FSK_Buffer[gFSKWriteIndex++] = word;
|
||||
}
|
||||
|
||||
gBeamRxWordCount = gFSKWriteIndex;
|
||||
gUpdateDisplay = true;
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (fskTarget == 1)
|
||||
AIRCOPY_StorePacket();
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
if (fskTarget == 2)
|
||||
BEAM_StorePacket();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1446,7 +1454,7 @@ void CheckKeys(void)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (gScreenToDisplay == DISPLAY_AIRCOPY && gAircopyState != AIRCOPY_READY){
|
||||
if (gScreenToDisplay == DISPLAY_AIRCOPY && gAircopyState == AIRCOPY_TRANSFER){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -1750,7 +1758,7 @@ void APP_TimeSlice10ms(void)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (gScreenToDisplay == DISPLAY_AIRCOPY && gAircopyState == AIRCOPY_TRANSFER && gAirCopyIsSendMode == 1) {
|
||||
if (gScreenToDisplay == DISPLAY_AIRCOPY && gAircopyState == AIRCOPY_TRANSFER) {
|
||||
if (!AIRCOPY_SendMessage()) {
|
||||
GUI_DisplayScreen();
|
||||
}
|
||||
|
||||
+2
-11
@@ -69,7 +69,6 @@ static_assert(sizeof(BEAM_Payload_t) <= 64);
|
||||
BEAM_Mode_t gBeamMode = BEAM_MODE_TX;
|
||||
BEAM_Status_t gBeamStatus = BEAM_STATUS_READY;
|
||||
uint16_t gBeamCopiedChannel = 0xFFFFu;
|
||||
uint8_t gBeamRxWordCount;
|
||||
bool gBeamActive;
|
||||
|
||||
static VFO_Info_t gBeamRadioVfo;
|
||||
@@ -120,7 +119,6 @@ static void BEAM_SendPacket(void)
|
||||
payload->dtmf_decoding_enable = vfo->DTMF_DECODING_ENABLE;
|
||||
#endif
|
||||
payload->step_setting = vfo->STEP_SETTING;
|
||||
payload->scrambling_type = vfo->SCRAMBLING_TYPE;
|
||||
payload->band = vfo->Band;
|
||||
payload->scanlist = vfo->SCANLIST_PARTICIPATION;
|
||||
payload->compander = vfo->Compander;
|
||||
@@ -192,18 +190,16 @@ static void BEAM_SavePayloadToFirstFreeChannel(const BEAM_Payload_t *payload)
|
||||
#endif
|
||||
vfo.STEP_SETTING = payload->step_setting < STEP_N_ELEM ? payload->step_setting : STEP_12_5kHz;
|
||||
vfo.StepFrequency = gStepFrequencyTable[vfo.STEP_SETTING];
|
||||
vfo.SCRAMBLING_TYPE = payload->scrambling_type;
|
||||
vfo.SCANLIST_PARTICIPATION = payload->scanlist;
|
||||
vfo.Compander = payload->compander;
|
||||
|
||||
memcpy(vfo.Name, payload->name, sizeof(vfo.Name));
|
||||
vfo.Name[sizeof(vfo.Name) - 1] = '\0';
|
||||
|
||||
RADIO_ApplyOffset(&vfo);
|
||||
RADIO_ConfigureSquelchAndOutputPower(&vfo);
|
||||
|
||||
SETTINGS_SaveChannel(channel, gEeprom.TX_VFO, &vfo, 3);
|
||||
#ifndef ENABLE_KEEP_MEM_NAME
|
||||
SETTINGS_SaveChannelName(channel, vfo.Name);
|
||||
#endif
|
||||
|
||||
gBeamCopiedChannel = channel;
|
||||
gBeamStatus = BEAM_STATUS_RX_SAVED;
|
||||
@@ -221,7 +217,6 @@ static void BEAM_KeyMenu(void)
|
||||
} else {
|
||||
gBeamStatus = BEAM_STATUS_RX_WAIT;
|
||||
gBeamCopiedChannel = 0xFFFFu;
|
||||
gBeamRxWordCount = 0;
|
||||
gFSKWriteIndex = 0;
|
||||
BK4819_PrepareFSKReceive();
|
||||
}
|
||||
@@ -249,7 +244,6 @@ void ACTION_Beam(void)
|
||||
gBeamMode = BEAM_MODE_TX;
|
||||
gBeamStatus = BEAM_STATUS_READY;
|
||||
gBeamCopiedChannel = 0xFFFFu;
|
||||
gBeamRxWordCount = 0;
|
||||
gBeamActive = true;
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
}
|
||||
@@ -267,7 +261,6 @@ void BEAM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
case KEY_DOWN:
|
||||
gBeamMode ^= 1; // (gBeamMode == BEAM_MODE_TX) ? BEAM_MODE_RX : BEAM_MODE_TX
|
||||
gBeamStatus = BEAM_STATUS_READY;
|
||||
gBeamRxWordCount = 0;
|
||||
break;
|
||||
case KEY_MENU:
|
||||
BEAM_KeyMenu();
|
||||
@@ -290,7 +283,6 @@ void BEAM_StorePacket(void)
|
||||
if (gFSKWriteIndex < 36)
|
||||
return;
|
||||
|
||||
gBeamRxWordCount = gFSKWriteIndex;
|
||||
gFSKWriteIndex = 0;
|
||||
|
||||
const uint16_t Status = BK4819_ReadRegister(BK4819_REG_0B);
|
||||
@@ -315,7 +307,6 @@ void BEAM_StorePacket(void)
|
||||
|
||||
error:
|
||||
gBeamStatus = BEAM_STATUS_ERROR;
|
||||
gBeamRxWordCount = 0;
|
||||
gUpdateDisplay = true;
|
||||
BACKLIGHT_TurnOn();
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ typedef enum {
|
||||
extern BEAM_Mode_t gBeamMode;
|
||||
extern BEAM_Status_t gBeamStatus;
|
||||
extern uint16_t gBeamCopiedChannel;
|
||||
extern uint8_t gBeamRxWordCount;
|
||||
extern bool gBeamActive;
|
||||
|
||||
void ACTION_Beam(void);
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ uint8_t gUnlockAllTxConfCnt;
|
||||
//
|
||||
EEPROM_ReadBuffer(0x1F88, &misc, 8);
|
||||
misc.BK4819_XtalFreqLow = value;
|
||||
EEPROM_WriteBuffer(0x1F88, &misc);
|
||||
EEPROM_WriteBuffer(0x1F88, &misc, 8);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -486,7 +486,7 @@ static void CMD_051D(uint32_t Port, const uint8_t *pBuffer)
|
||||
|
||||
if ((Offset < 0x0E98 || Offset >= 0x0EA0) || !bIsInLockScreen || pCmd->bAllowPassword)
|
||||
{
|
||||
EEPROM_WriteBuffer(Offset, &pCmd->Data[i * 8U]);
|
||||
EEPROM_WriteBuffer(Offset, &pCmd->Data[i * 8U], 8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-9
@@ -39,25 +39,30 @@ void EEPROM_ReadBuffer(uint16_t Address, void *pBuffer, uint8_t Size)
|
||||
I2C_Stop();
|
||||
}
|
||||
|
||||
void EEPROM_WriteBuffer(uint16_t Address, const void *pBuffer)
|
||||
void EEPROM_WriteBuffer(uint16_t Address, const void *pBuffer, uint8_t Size)
|
||||
{
|
||||
if (pBuffer == NULL || Address >= 0x2000)
|
||||
if (pBuffer == NULL)
|
||||
return;
|
||||
|
||||
|
||||
uint8_t buffer[8];
|
||||
EEPROM_ReadBuffer(Address, buffer, 8);
|
||||
if (memcmp(pBuffer, buffer, 8) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (Size >= sizeof(buffer) && Address < 0x2000)
|
||||
{
|
||||
EEPROM_ReadBuffer(Address, buffer, sizeof(buffer));
|
||||
if (memcmp(pBuffer, buffer, sizeof(buffer)) != 0)
|
||||
{
|
||||
I2C_Start();
|
||||
I2C_Write(0xA0);
|
||||
I2C_Write((Address >> 8) & 0xFF);
|
||||
I2C_Write((Address >> 0) & 0xFF);
|
||||
I2C_WriteBuffer(pBuffer, 8);
|
||||
I2C_WriteBuffer(pBuffer, sizeof(buffer));
|
||||
I2C_Stop();
|
||||
|
||||
// give the EEPROM time to burn the data in (apparently takes 5ms)
|
||||
SYSTEM_DelayMs(8);
|
||||
}
|
||||
|
||||
Address += sizeof(buffer);
|
||||
pBuffer += sizeof(buffer);
|
||||
Size -= sizeof(buffer);
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -20,7 +20,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
void EEPROM_ReadBuffer(uint16_t Address, void *pBuffer, uint8_t Size);
|
||||
void EEPROM_WriteBuffer(uint16_t Address, const void *pBuffer);
|
||||
void EEPROM_WriteBuffer(uint16_t Address, const void *pBuffer, uint8_t Size);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -110,11 +110,8 @@ void EEPROM_ReadBuffer(uint16_t Address, void *pBuffer, uint8_t Size)
|
||||
}
|
||||
}
|
||||
|
||||
void EEPROM_WriteBuffer(uint16_t Address, const void *pBuffer)
|
||||
void EEPROM_WriteBuffer(uint16_t Address, const void *pBuffer, uint8_t Size)
|
||||
{
|
||||
// Write 8 bytes!!
|
||||
|
||||
uint16_t Size = 8;
|
||||
while (Size)
|
||||
{
|
||||
uint32_t PY_Addr;
|
||||
|
||||
@@ -144,9 +144,6 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
uint8_t gDW = 0;
|
||||
uint8_t gCB = 0;
|
||||
bool gSaveRxMode = false;
|
||||
uint8_t crc[15] = { 0 };
|
||||
uint8_t lErrorsDuringAirCopy = 0;
|
||||
uint8_t gAircopyStep = 0;
|
||||
uint8_t gAircopyCurrentMapIndex = 0;
|
||||
bool gAirCopyBootMode = 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
|
||||
@@ -219,9 +219,6 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
extern uint8_t gDW;
|
||||
extern uint8_t gCB;
|
||||
extern bool gSaveRxMode;
|
||||
extern uint8_t crc[15];
|
||||
extern uint8_t lErrorsDuringAirCopy;
|
||||
extern uint8_t gAircopyStep;
|
||||
extern uint8_t gAircopyCurrentMapIndex;
|
||||
extern bool gAirCopyBootMode;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
|
||||
+29
-66
@@ -27,14 +27,6 @@
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
|
||||
static void set_bit(uint8_t* array, int bit_index) {
|
||||
array[bit_index / 8] |= (1 << (bit_index % 8));
|
||||
}
|
||||
|
||||
static int get_bit(uint8_t* array, int bit_index) {
|
||||
return (array[bit_index / 8] >> (bit_index % 8)) & 1;
|
||||
}
|
||||
|
||||
void UI_DisplayAircopy(void)
|
||||
{
|
||||
char String[16];
|
||||
@@ -46,9 +38,10 @@ void UI_DisplayAircopy(void)
|
||||
pPrintStr = "AIR COPY(RDY)";
|
||||
} else if (gAircopyState == AIRCOPY_TRANSFER) {
|
||||
pPrintStr = "AIR COPY";
|
||||
} else if (gAircopyState == AIRCOPY_COMPLETE) {
|
||||
pPrintStr = "AIR COPY OK";
|
||||
} else {
|
||||
pPrintStr = "AIR COPY(CMP)";
|
||||
gAircopyState = AIRCOPY_READY;
|
||||
pPrintStr = "AIR COPY FAIL";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 127, 0, 8);
|
||||
@@ -67,19 +60,15 @@ void UI_DisplayAircopy(void)
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
|
||||
// Get the current map and calculate percentage based on its total blocks
|
||||
const AIRCOPY_TransferMap_t *currentMap = AIRCOPY_GetCurrentMap();
|
||||
const uint8_t totalBlocks = AIRCOPY_GetTotalBlocks();
|
||||
uint16_t doneBlocks = gAirCopyBlockNumber;
|
||||
|
||||
uint16_t doneBlocks = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
if (doneBlocks > currentMap->total_blocks)
|
||||
doneBlocks = currentMap->total_blocks;
|
||||
if (doneBlocks > totalBlocks)
|
||||
doneBlocks = totalBlocks;
|
||||
|
||||
// Draw memory selection
|
||||
if (gAircopyState == AIRCOPY_READY)
|
||||
{
|
||||
doneBlocks = 0;
|
||||
|
||||
if(gAircopyCurrentMapIndex < AIRCOPY_NUM_BANKS) {
|
||||
sprintf(String, "MEM %03u - %03u", (gAircopyCurrentMapIndex * 128) + 1, (gAircopyCurrentMapIndex + 1) * 128);
|
||||
} else {
|
||||
@@ -89,65 +78,39 @@ void UI_DisplayAircopy(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t percent = (doneBlocks * 10000) / currentMap->total_blocks;
|
||||
uint16_t percent = (doneBlocks * 10000) / totalBlocks;
|
||||
const unsigned displayedErrors = gErrorsDuringAirCopy > 99u
|
||||
? 99u
|
||||
: gErrorsDuringAirCopy;
|
||||
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%02u.%02u%% E:%d", percent / 100, percent % 100, gErrorsDuringAirCopy);
|
||||
if (gAircopyState == AIRCOPY_COMPLETE || gAircopyState == AIRCOPY_FAILED) {
|
||||
sprintf(String, "%s %u/%u %s:%u",
|
||||
gAircopyState == AIRCOPY_COMPLETE ? "OK" : "KO",
|
||||
doneBlocks, totalBlocks,
|
||||
gAirCopyIsSendMode ? "RT" : "ER",
|
||||
displayedErrors);
|
||||
} else if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RX:%02u.%02u ER:%u", percent / 100, percent % 100,
|
||||
displayedErrors);
|
||||
} else {
|
||||
sprintf(String, "SND:%02u.%02u%%", percent / 100, percent % 100);
|
||||
sprintf(String, "TX:%02u.%02u RT:%u", percent / 100, percent % 100,
|
||||
displayedErrors);
|
||||
}
|
||||
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
{
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= AIRCOPY_BAR_WIDTH + 2; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
}
|
||||
|
||||
gFrameBuffer[4][3] = 0x81;
|
||||
// Match the former DDA gauge exactly, including its partial first pixel.
|
||||
const uint8_t filled = (doneBlocks * AIRCOPY_BAR_WIDTH + totalBlocks - 1u)
|
||||
/ totalBlocks;
|
||||
for (uint8_t col = 0; col < AIRCOPY_BAR_WIDTH; col++)
|
||||
gFrameBuffer[4][col + 4] = col < filled ? 0xBD : 0x81;
|
||||
gFrameBuffer[4][124] = 0x81;
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
}
|
||||
}
|
||||
|
||||
if (doneBlocks > 0)
|
||||
{
|
||||
// Track CRC errors per real block index
|
||||
if (gErrorsDuringAirCopy != lErrorsDuringAirCopy)
|
||||
{
|
||||
// Mark the last processed block as faulty
|
||||
set_bit(crc, doneBlocks - 1);
|
||||
lErrorsDuringAirCopy = gErrorsDuringAirCopy;
|
||||
}
|
||||
|
||||
uint16_t b = 0;
|
||||
uint16_t fraction_accumulator = 0;
|
||||
|
||||
for (uint8_t col = 0; col < AIRCOPY_BAR_WIDTH; col++)
|
||||
{
|
||||
bool processed = (b < doneBlocks);
|
||||
bool error = processed && get_bit(crc, b);
|
||||
|
||||
if (!processed)
|
||||
gFrameBuffer[4][col + 4] = 0x81; // not yet processed
|
||||
else if (error)
|
||||
gFrameBuffer[4][col + 4] = 0x81; // error gap (intentional hole)
|
||||
else
|
||||
gFrameBuffer[4][col + 4] = 0xBD; // ok filled
|
||||
|
||||
// DDA/Bresenham algorythm
|
||||
fraction_accumulator += currentMap->total_blocks;
|
||||
while (fraction_accumulator >= AIRCOPY_BAR_WIDTH) {
|
||||
fraction_accumulator -= AIRCOPY_BAR_WIDTH;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user