mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 11:08:20 +00:00
208 lines
6.4 KiB
C
208 lines
6.4 KiB
C
/* Copyright 2025 muzkr https://github.com/muzkr
|
|
* Copyright 2023 Dual Tachyon
|
|
* https://github.com/DualTachyon
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "py32f071_ll_bus.h"
|
|
#include "py32f071_ll_system.h"
|
|
#include "py32f071_ll_dma.h"
|
|
#include "py32f071_ll_gpio.h"
|
|
#include "py32f071_ll_rcc.h"
|
|
#include "py32f071_ll_usart.h"
|
|
|
|
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
|
#include "driver/keyboard.h"
|
|
#endif
|
|
|
|
#define USARTx USART1
|
|
#define DMA_CHANNEL LL_DMA_CHANNEL_2
|
|
|
|
// CRITICAL FIX: Define UART TX timeout
|
|
// If UART TX buffer doesn't clear within this many iterations, skip byte and continue
|
|
// Prevents permanent freeze if UART is stuck
|
|
#define UART_TX_TIMEOUT_ITERATIONS 10000
|
|
|
|
static bool UART_IsLogEnabled;
|
|
uint8_t UART_DMA_Buffer[256];
|
|
|
|
void UART_Init(void)
|
|
{
|
|
// PA9 TX
|
|
// PA10 RX
|
|
|
|
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
|
|
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
|
|
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_SYSCFG);
|
|
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_USART1);
|
|
|
|
// Pins
|
|
do
|
|
{
|
|
LL_GPIO_InitTypeDef GPIO_InitStruct;
|
|
LL_GPIO_StructInit(&GPIO_InitStruct);
|
|
GPIO_InitStruct.Pin = LL_GPIO_PIN_9 | LL_GPIO_PIN_10;
|
|
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
|
GPIO_InitStruct.Alternate = LL_GPIO_AF1_USART1;
|
|
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
|
|
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
|
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
|
|
|
|
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
|
} while (0);
|
|
|
|
// DMA
|
|
do
|
|
{
|
|
LL_DMA_DisableChannel(DMA1, DMA_CHANNEL);
|
|
|
|
LL_DMA_InitTypeDef DMA_InitStruct;
|
|
LL_DMA_StructInit(&DMA_InitStruct);
|
|
|
|
DMA_InitStruct.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
|
|
DMA_InitStruct.Mode = LL_DMA_MODE_CIRCULAR;
|
|
DMA_InitStruct.PeriphOrM2MSrcAddress = LL_USART_DMA_GetRegAddr(USARTx);
|
|
DMA_InitStruct.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
|
DMA_InitStruct.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
|
|
DMA_InitStruct.MemoryOrM2MDstAddress = (uint32_t)UART_DMA_Buffer;
|
|
DMA_InitStruct.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
|
|
DMA_InitStruct.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
|
DMA_InitStruct.NbData = sizeof(UART_DMA_Buffer);
|
|
DMA_InitStruct.Priority = LL_DMA_PRIORITY_HIGH;
|
|
|
|
LL_DMA_Init(DMA1, DMA_CHANNEL, &DMA_InitStruct);
|
|
|
|
LL_SYSCFG_SetDMARemap(DMA1, DMA_CHANNEL, LL_SYSCFG_DMA_MAP_USART1_RD);
|
|
|
|
} while (0);
|
|
|
|
LL_APB1_GRP2_ForceReset(LL_APB1_GRP2_PERIPH_USART1);
|
|
LL_APB1_GRP2_ReleaseReset(LL_APB1_GRP2_PERIPH_USART1);
|
|
|
|
// USART
|
|
do
|
|
{
|
|
LL_USART_Disable(USARTx);
|
|
|
|
LL_USART_InitTypeDef USART_InitStruct;
|
|
LL_USART_StructInit(&USART_InitStruct);
|
|
|
|
USART_InitStruct.BaudRate = 38400;
|
|
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
|
|
LL_USART_Init(USARTx, &USART_InitStruct);
|
|
|
|
LL_USART_EnableDMAReq_RX(USARTx);
|
|
|
|
} while (0);
|
|
|
|
LL_DMA_EnableChannel(DMA1, DMA_CHANNEL);
|
|
LL_USART_Enable(USARTx);
|
|
LL_USART_TransmitData8(USARTx, 0);
|
|
}
|
|
|
|
#ifdef ENABLE_AIRCOPY_UART
|
|
bool UART_SetBaudRate(uint32_t BaudRate)
|
|
{
|
|
uint32_t timeout = UART_TX_TIMEOUT_ITERATIONS;
|
|
LL_RCC_ClocksTypeDef clocks;
|
|
|
|
// Never change BRR while the final byte of a frame is still on the wire.
|
|
while (!LL_USART_IsActiveFlag_TC(USARTx) && timeout > 0u)
|
|
timeout--;
|
|
|
|
if (!LL_USART_IsActiveFlag_TC(USARTx))
|
|
return false;
|
|
|
|
LL_RCC_GetSystemClocksFreq(&clocks);
|
|
if (BaudRate == 0u || clocks.PCLK1_Frequency == LL_RCC_PERIPH_FREQUENCY_NO)
|
|
return false;
|
|
|
|
LL_USART_Disable(USARTx);
|
|
#if defined(USART_CR3_OVER8)
|
|
LL_USART_SetBaudRate(USARTx, clocks.PCLK1_Frequency,
|
|
LL_USART_GetOverSampling(USARTx), BaudRate);
|
|
#else
|
|
LL_USART_SetBaudRate(USARTx, clocks.PCLK1_Frequency, BaudRate);
|
|
#endif
|
|
LL_USART_Enable(USARTx);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
void UART_Send(const void *pBuffer, uint32_t Size)
|
|
{
|
|
const uint8_t *pData = (const uint8_t *)pBuffer;
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < Size; i++)
|
|
{
|
|
// CRITICAL FIX: Add timeout to UART TX busy-wait
|
|
// Original: while (!LL_USART_IsActiveFlag_TXE(USARTx)) ;
|
|
// Problem: If UART TX stuck or disconnected, this is infinite loop
|
|
// Result: Radio freeze for 100ms+ while trying to send one character
|
|
//
|
|
// Solution: Add iteration counter timeout
|
|
// If TXE flag doesn't set within timeout iterations, skip byte and continue
|
|
// This caps maximum freeze at ~10ms per UART_Send() call
|
|
|
|
uint32_t timeout = UART_TX_TIMEOUT_ITERATIONS;
|
|
while (!LL_USART_IsActiveFlag_TXE(USARTx) && timeout > 0)
|
|
{
|
|
timeout--;
|
|
}
|
|
|
|
// Send byte only if TXE flag is set
|
|
// If timeout occurred, skip this byte and continue
|
|
if (timeout > 0)
|
|
{
|
|
LL_USART_TransmitData8(USARTx, pData[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UART_LogSend(const void *pBuffer, uint32_t Size)
|
|
{
|
|
if (UART_IsLogEnabled) {
|
|
UART_Send(pBuffer, Size);
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
|
bool UART_IsCableConnected(void) {
|
|
static uint8_t read_ptr = 0;
|
|
static ParseState_t state = STATE_IDLE;
|
|
|
|
bool connected = false;
|
|
|
|
// DMA write position: NbData counts DOWN from 256
|
|
uint8_t write_ptr = (uint8_t)(sizeof(UART_DMA_Buffer) -
|
|
LL_DMA_GetDataLength(DMA1, DMA_CHANNEL));
|
|
|
|
uint8_t processed = 0;
|
|
while (read_ptr != write_ptr && processed < sizeof(UART_DMA_Buffer))
|
|
{
|
|
uint8_t b = UART_DMA_Buffer[read_ptr++];
|
|
// read_ptr wraps naturally at 256 since it's uint8_t
|
|
processed++;
|
|
|
|
if(KEYBOARD_ProcessProtocolByte(&state, b))
|
|
connected = true;
|
|
}
|
|
|
|
return connected;
|
|
}
|
|
#endif
|