/** ****************************************************************************** * @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 */