/**
******************************************************************************
* @file main.c
* @author MCU Application Team
* @brief 音箱控制面板 - 应用主逻辑
******************************************************************************
* @attention
*
*
© Copyright (c) 2023 Puya Semiconductor Co.
* All rights reserved.
*
* This software component is licensed by Puya under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
* @attention
*
* © Copyright (c) 2016 STMicroelectronics.
* All rights reserved.
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include
#include "app_state.h"
#include "led_driver.h"
#include "battery_display.h"
#include "input_scan.h"
#include "uart_protocol.h"
/* Private define ------------------------------------------------------------*/
/* ========== TIM3 定时参数 ========== */
/* 系统时钟 48MHz (PLL), 预分频0 => 48MHz, Period=4799 => 10kHz = 100us */
#define TIM3_PRESCALER 0
#define TIM3_PERIOD 4799
/* 中断内时间基准 */
#define TICK_1MS 10u /* 1ms = 10 * 100us */
/* ========== 长按计时 ========== */
#define BOTH_PRESS_MS 10000u /* 两编码器键同时长按10s软复位 */
#define IDLE_QUERY_MS 30000u /* 空闲30s后主动查询 */
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef TimHandle;
/* UART2 句柄由 BSP 库在 py32f030xx_Start_Kit.c 中定义 */
extern UART_HandleTypeDef DebugUartHandle;
/* ========== 共享状态变量定义 (声明在 app_state.h) ========== */
/* 主从同步变量 (来自主板) */
volatile uint8_t g_inputSource = 0u;
volatile uint8_t g_btConnState = 0u;
volatile uint8_t g_audioPlaying = 0u;
volatile uint8_t g_batteryPct = 0u;
volatile uint8_t g_chgFull = 0u;
volatile uint8_t g_chgIng = 0u;
volatile uint8_t g_dacMute = 0u;
/* 音量等级 (本地操作值) */
volatile int16_t g_volLRLevel = 0;
volatile int16_t g_volBassLevel = 0;
/* 异步发送标志 (由中断置位, 主循环处理) */
volatile uint8_t g_flagSendVolLR = 0u;
volatile uint8_t g_flagSendVolBass = 0u;
volatile uint8_t g_flagSendDacUnmute = 0u;
/* 面板本地状态 */
volatile uint8_t g_dataReceived = 0u;
volatile uint8_t g_mutedByBtn = 0u;
/* 各状态接收标志 (用于开机全亮无缝切换) */
volatile uint8_t g_volLRReceived = 0u;
volatile uint8_t g_volBassReceived = 0u;
volatile uint8_t g_batteryReceived = 0u;
volatile uint8_t g_sourceReceived = 0u;
/* 系统毫秒计时 */
volatile uint32_t g_sysMs = 0u;
/* 空闲查询计时 */
volatile uint16_t g_idleTimer = 0u;
volatile uint8_t g_userActive = 0u;
/* Private function prototypes -----------------------------------------------*/
static void APP_SystemClockConfig(void);
static void APP_GPIO_Init(void);
static void APP_UART_Init(void);
static void APP_TIM3_Init(void);
static void SystemReadyIndicate(void);
/* ========== 主函数 ========== */
/**
* @brief Main program - 音箱主板控制面板
*/
int main(void)
{
HAL_Init();
APP_SystemClockConfig();
APP_GPIO_Init();
APP_UART_Init();
LED_InitControlBlock();
InputScan_Init();
APP_TIM3_Init();
/* 上电所有灯全亮3秒, 等待主板开机上报 (LED11除外, 等收到数据再显示) */
for (uint8_t i = 0; i < 13u; i++)
{
if (i == 10u)
LED_SetState(i, 1u); /* LED11全亮, 收到主板上报后更新 */
else
LED_SetState(i, 3u); /* 其他反向并联LED双灯同亮 */
}
LED_ApplyAllStates();
/* 等待3秒让主板启动, 期间每秒查询一次 */
for (uint8_t i = 0; i < 3u; i++)
{
HAL_Delay(1000u);
/* 处理延时期间收到的帧 */
while (UART_IsFrameReady())
{
UART_ClearFrameReady();
ProcessReceivedFrame();
}
/* 如果还没收到数据, 主动查询 */
if (!g_dataReceived)
{
SendQueryAll();
HAL_Delay(200u);
while (UART_IsFrameReady())
{
UART_ClearFrameReady();
ProcessReceivedFrame();
}
}
else
{
break; /* 收到数据了, 提前结束等待 */
}
}
/* 系统就绪指示 */
SystemReadyIndicate();
while (1)
{
/* ---- UART 接收看门狗: 检测并恢复卡死的接收 ---- */
{
static uint32_t s_uartWdMs = 0u;
if ((g_sysMs - s_uartWdMs) >= 200u)
{
s_uartWdMs = g_sysMs;
/* 如果 RxState 不是 BUSY_RX, 说明接收已经停止 */
if (DebugUartHandle.RxState != HAL_UART_STATE_BUSY_RX)
{
/* 清除所有错误标志 */
__HAL_UART_CLEAR_OREFLAG(&DebugUartHandle);
__HAL_UART_CLEAR_FEFLAG(&DebugUartHandle);
__HAL_UART_CLEAR_NEFLAG(&DebugUartHandle);
__HAL_UART_CLEAR_PEFLAG(&DebugUartHandle);
/* 强制恢复 RxState 并重启接收 */
DebugUartHandle.RxState = HAL_UART_STATE_READY;
HAL_UART_Receive_IT(&DebugUartHandle, &g_rxByte, 1u);
}
}
}
/* ---- 处理所有待处理协议帧 ---- */
while (UART_IsFrameReady())
{
UART_ClearFrameReady();
ProcessReceivedFrame();
}
/* ---- 异步发送 UART 指令 (脱离中断执行) ---- */
if (g_flagSendVolLR)
{
static int16_t s_dispLRLvl = -1;
if (s_dispLRLvl != g_volLRLevel)
{
s_dispLRLvl = g_volLRLevel;
UpdateVolumeLEDs(); /* 立即更新本地 UI,保证手感无延迟 */
}
/* 限流向主板发送 (50ms) */
static uint32_t s_lastTxVolLRMs = 0u;
if ((g_sysMs - s_lastTxVolLRMs) >= 50u)
{
g_flagSendVolLR = 0u;
s_lastTxVolLRMs = g_sysMs;
SendSetVolLR((uint8_t)g_volLRLevel);
}
}
if (g_flagSendVolBass)
{
static int16_t s_dispBassLvl = -1;
if (s_dispBassLvl != g_volBassLevel)
{
s_dispBassLvl = g_volBassLevel;
UpdateVolumeLEDs();
}
static uint32_t s_lastTxVolBassMs = 0u;
if ((g_sysMs - s_lastTxVolBassMs) >= 50u)
{
g_flagSendVolBass = 0u;
s_lastTxVolBassMs = g_sysMs;
SendSetVolBass((uint8_t)g_volBassLevel);
}
}
if (g_flagSendDacUnmute)
{
g_flagSendDacUnmute = 0u;
SendSetDacMute(0x01u); /* DAC_UNMUTE */
}
/* ---- 处理按键事件 ---- */
if (g_swKeyEdge)
{
g_swKeyEdge = 0u;
/* SW_KEY(PF3): 切换音源 BT <-> AUX (本地切换, 不等主板上报) */
if (g_inputSource == 0x01u) /* BT=0x01 */
g_inputSource = 0x00u; /* AUX=0x00 */
else
g_inputSource = 0x01u;
SendSetInputSrc(g_inputSource);
UpdateSourceLED();
g_userActive = 1u;
g_idleTimer = 0u;
}
if (g_swKeyLongPress == 1u)
{
g_swKeyLongPress = 2u; /* 标记已处理, 防止重复发送 */
SendBTDisconnect();
g_userActive = 1u;
g_idleTimer = 0u;
}
if (g_sw1KeyEdge)
{
g_sw1KeyEdge = 0u;
/* SW1_KEY: 低音静音 */
g_volBassLevel = 0;
UpdateVolumeLEDs();
SendBassMute();
g_userActive = 1u;
g_idleTimer = 0u;
}
if (g_sw2KeyEdge)
{
g_sw2KeyEdge = 0u;
/* SW2_KEY: 快速静音, 仅第一次按下有效 */
if (!g_mutedByBtn)
{
g_mutedByBtn = 1u;
SendSetDacMute(0x00u); /* 0x00 is Mute */
for (uint8_t i = 0; i < 10u; i++)
LED_SetState(i, 0u);
LED_ApplyAllStates();
}
g_userActive = 1u;
g_idleTimer = 0u;
}
/* ---- 双键同按10s软复位 (500ms防误触 + 10s = 10500ms) ---- */
if (g_bothPressFlag && g_bothPressMs >= (BOTH_PRESS_MS + 500u))
{
g_bothPressFlag = 0u;
g_bothPressMs = 0u;
/* 清除可能触发的静音标志 */
g_sw1KeyEdge = 0u;
g_sw2KeyEdge = 0u;
SendSoftReset();
HAL_Delay(100u);
NVIC_SystemReset();
}
/* ---- 空闲30s后主动查询同步 ---- */
if (g_dataReceived && g_idleTimer >= IDLE_QUERY_MS)
{
g_idleTimer = 0u;
SendQueryAll();
}
}
}
/* ========== 系统就绪指示 ========== */
static void SystemReadyIndicate(void)
{
printf("\r\n========================================\r\n");
printf(" Speaker Control Panel\r\n");
printf(" Waiting for mainboard data...\r\n");
printf("========================================\r\n");
}
/* ========== 硬件初始化 ========== */
/**
* @brief 系统时钟配置: HSI 24MHz -> PLL = 48MHz
*/
static void APP_SystemClockConfig(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_HSI
| RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
#if defined(RCC_HSIDIV_SUPPORT)
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
#endif
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_24MHz;
RCC_OscInitStruct.HSEState = RCC_HSE_OFF;
RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
APP_ErrorHandler();
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
APP_ErrorHandler();
}
/**
* @brief GPIO初始化 (所有引脚初始配置为浮空输入)
*/
static void APP_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
RCC->IOPENR |= RCC_IOPENR_GPIOFEN;
/* GPIOA: LED + 编码器 + SW2_KEY */
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4
| GPIO_PIN_8 | GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* GPIOB: LED + 编码器 + SW1_KEY */
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4
| GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SW1_KEY (PB0) */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* GPIOF: SW_KEY */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
}
/**
* @brief UART2初始化
*/
static void APP_UART_Init(void)
{
BSP_USART_Config();
/* 开启UART2中断接收, 缓冲区在 uart_protocol.c 中定义 */
HAL_UART_Receive_IT(&DebugUartHandle, &g_rxByte, 1u);
}
/**
* @brief TIM3初始化: 100us中断 (10kHz)
*/
static void APP_TIM3_Init(void)
{
TimHandle.Instance = TIM3;
TimHandle.Init.Period = TIM3_PERIOD;
TimHandle.Init.Prescaler = TIM3_PRESCALER;
TimHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
APP_ErrorHandler();
if (HAL_TIM_Base_Start_IT(&TimHandle) != HAL_OK)
APP_ErrorHandler();
}
/* ========== 中断回调 ========== */
/**
* @brief TIM3 周期中断回调: 每 100us 执行一次
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint32_t s_tick = 0u;
s_tick++;
/* 每 1ms 更新系统毫秒计数 */
if ((s_tick % 10u) == 0u) g_sysMs++;
/* LED 定时翻转 (状态3/4/5) */
LED_Tick100us();
/* 每 1ms 扫描按键/编码器/电池时间槽 */
if ((s_tick % TICK_1MS) == 0u)
{
ScanButtons();
ScanEncoders();
BatterySlotUpdate();
/* 双键同按计时: 前500ms防误触 */
if (g_bothPressFlag)
{
if (g_bothPressMs < (BOTH_PRESS_MS + 500u))
g_bothPressMs++;
}
/* 空闲计时 */
if (g_userActive)
{
if (++g_idleTimer >= 200u) /* 200ms无操作认为活动结束 */
{
g_userActive = 0u;
g_idleTimer = 0u;
}
}
else if (g_dataReceived)
{
if (g_idleTimer < IDLE_QUERY_MS)
g_idleTimer++;
}
}
}
/**
* @brief USART2 接收完成中断回调 (HAL库)
* 每次重新开启接收前清除溢出错误标志, 防止 ORE 卡死
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
UART_RxByteCallback(g_rxByte);
/* 清除可能的溢出错误标志, 防止 ORE 导致接收停止 */
__HAL_UART_CLEAR_OREFLAG(huart);
HAL_UART_Receive_IT(&DebugUartHandle, &g_rxByte, 1u);
}
}
/**
* @brief UART 错误中断回调 (HAL库)
* 当发生 ORE/FE/NE/PE 错误时, HAL 会调用此函数而非 RxCpltCallback.
* 必须在此处重新启动接收, 否则 UART 接收将永久停止.
*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
/* 清除所有错误标志 */
__HAL_UART_CLEAR_OREFLAG(huart);
__HAL_UART_CLEAR_FEFLAG(huart);
__HAL_UART_CLEAR_NEFLAG(huart);
__HAL_UART_CLEAR_PEFLAG(huart);
/* 重新启动中断接收, 恢复通讯 */
HAL_UART_Receive_IT(&DebugUartHandle, &g_rxByte, 1u);
}
}
/* ========== 错误处理 ========== */
void APP_ErrorHandler(void)
{
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
while (1)
{
}
}
#endif /* USE_FULL_ASSERT */