diff --git a/project/example/ble/ble_peripheral/app/src/PWM.h b/project/example/ble/ble_peripheral/app/src/PWM.h index 6db13bd..7c1af7a 100644 --- a/project/example/ble/ble_peripheral/app/src/PWM.h +++ b/project/example/ble/ble_peripheral/app/src/PWM.h @@ -147,8 +147,7 @@ void Bridge_Init(void); void Bridge_Off(void); void Bridge_Service_1ms(void); void Bridge_Deadtime_Expired(void); -uint8_t Bridge_PauseForFlash(void); -void Bridge_ResumeAfterFlash(uint8_t paused); +uint8_t Bridge_PrepareFlash(uint8_t *keep_timer3); #ifdef __cplusplus } #endif diff --git a/project/example/ble/ble_peripheral/app/src/bridge_pwm.c b/project/example/ble/ble_peripheral/app/src/bridge_pwm.c index 0b3a82c..5be56ef 100644 --- a/project/example/ble/ble_peripheral/app/src/bridge_pwm.c +++ b/project/example/ble/ble_peripheral/app/src/bridge_pwm.c @@ -246,26 +246,42 @@ void Bridge_Off(void) output_mode = BRIDGE_MODE_OFF; } -uint8_t Bridge_PauseForFlash(void) +uint8_t Bridge_PrepareFlash(uint8_t *keep_timer3) { - /* - * Single-color output is hardware PWM and remains stable while flash - * masks interrupts. Dual-color output is Timer3-driven, so force both - * bridge inputs low before flash can stretch an active direction pulse. - */ - if (!bridge_ready || output_mode != BRIDGE_MODE_DUAL) { + if (keep_timer3 == 0) { return 0U; } - Bridge_Off(); - return 1U; -} - -void Bridge_ResumeAfterFlash(uint8_t paused) -{ - if (paused) { - Bridge_Service_1ms(); + *keep_timer3 = 0U; + if (!bridge_ready) { + return 1U; } + + /* + * Hardware PWM single-color output needs no interrupt while flash is + * busy. A stable dual-color frame may keep Timer3 enabled because its + * complete normal interrupt path is located in RAM. + */ + if (output_mode == BRIDGE_MODE_SINGLE && + timer_stage == BRIDGE_STAGE_IDLE) { + return 1U; + } + + if (output_mode == BRIDGE_MODE_DUAL && + timer_stage >= BRIDGE_STAGE_START_W && + timer_stage <= BRIDGE_STAGE_END_C) { + *keep_timer3 = 1U; + return 1U; + } + + if (output_mode == BRIDGE_MODE_OFF && + timer_stage == BRIDGE_STAGE_IDLE && + W_PWM_duty == 0U && C_PWM_duty == 0U) { + return 1U; + } + + /* Retry on the next 1 ms task after a bridge mode transition settles. */ + return 0U; } __RAM_CODE void Bridge_Deadtime_Expired(void) diff --git a/project/example/ble/ble_peripheral/app/src/ota_flash_interface.c b/project/example/ble/ble_peripheral/app/src/ota_flash_interface.c index 2a7f87c..232c874 100644 --- a/project/example/ble/ble_peripheral/app/src/ota_flash_interface.c +++ b/project/example/ble/ble_peripheral/app/src/ota_flash_interface.c @@ -96,26 +96,53 @@ void ble_flash_handle(void) uint8_t app_data[FLASH_PAGE_SIZE] = {0}; +__RAM_CODE static void app_user_data_flash_commit(uint8_t keep_timer3) +{ + uint32_t saved_irq_enable; + uint32_t irq_temp; + uint32_t live_irq_mask; + + /* + * BLE_Handler executes from chip ROM and PendSV_Handler executes + * from RAM, so Bluetooth timing can remain active while flash is + * busy. The stable dual-color Timer3 path also executes from RAM. + * Block only ordinary XIP-backed peripheral/task interrupts. + */ + irq_temp = __disable_irq(); + saved_irq_enable = NVIC->ISER[0]; + NVIC->ICER[0] = 0xFFFFFFFFUL; + live_irq_mask = (1UL << BLE_IRQn); + if(keep_timer3) + live_irq_mask |= (1UL << TIMER3_IRQn); + NVIC->ISER[0] = live_irq_mask; + if(!irq_temp) + __enable_irq(); + + FMC_SPI_Flash_Erase_Page(0x1E000); + FMC_SPI_Flash_WritePage(0x1E000, app_data, FLASH_PAGE_SIZE); + + __disable_irq(); + NVIC->ICER[0] = 0xFFFFFFFFUL; + NVIC->ISER[0] = saved_irq_enable; + if(!irq_temp) + __enable_irq(); +} + //uint16_t app_op_flash_start = 10000; void app_op_flash(void) { - uint8_t bridge_paused; + uint8_t keep_timer3; if(op_flash.op_state == OP_IDEL&&app_op_flash_flag ==0) { /* - * Commit user data as one transaction. Timer3 cannot advance - * dual-color PWM while flash masks interrupts, so hold the - * H-bridge at 00 and resume from a fresh complete frame. - * Hardware PWM single-color output does not need pausing. + * Wait out a brief bridge mode handoff, then save immediately + * without stopping Bluetooth or the active light waveform. */ - GLOBAL_INT_DISABLE(); - bridge_paused = Bridge_PauseForFlash(); - FMC_SPI_Flash_Erase_Page(0x1E000); - FMC_SPI_Flash_WritePage(0x1E000, app_data, FLASH_PAGE_SIZE); - GLOBAL_INT_RESTORE(); + if(!Bridge_PrepareFlash(&keep_timer3)) + return; + app_user_data_flash_commit(keep_timer3); app_op_flash_flag = 2; - Bridge_ResumeAfterFlash(bridge_paused); } else if(op_flash.op_state == OP_IDEL &&app_op_flash_flag ==1) {