diff --git a/Robot_V2/device/Can.cpp b/Robot_V2/device/Can.cpp index eb765a6..598fc8e 100644 --- a/Robot_V2/device/Can.cpp +++ b/Robot_V2/device/Can.cpp @@ -8,6 +8,7 @@ #include "gd32c10x.h" #include "gd32c10x_can.h" #include "gd32c10x_dma.h" +#include "gd32c10x_fmc.h" #include "gd32c10x_gpio.h" #include "motor.h" #include "systick.h" @@ -611,10 +612,13 @@ void CAN_transmiter::communication_check() { Vel_kalman.init(0); // 角度滤波 } if (eigth_byte & 0x80 >> 7) { - prepare_deep_sleep(true); + app_prepare_deep_sleep(true); // CAN_Sleep(); } } else if (receive_message.rx_efid == CANFD_BOOT_ID) { + fmc_unlock(); + OTA_SET(n_APP_LATEST_EN_ADDR, OTA_NOT_AVAL); // 设置APP为不激活 + fmc_lock(); jumpToBOOT(); } else if (receive_message.rx_efid == 0x00000102) { volatile u32 *flash_ptr = (volatile u32 *)n_APP_LATEST_EN_ADDR; diff --git a/Robot_V2/main.cpp b/Robot_V2/main.cpp index 12356ac..8b98faf 100644 --- a/Robot_V2/main.cpp +++ b/Robot_V2/main.cpp @@ -56,12 +56,6 @@ u8 hardware_init(void) { unused_pin_init(); // 未使用引脚初始化 USART0_init(); // 调试串口初始化 motor_init(); // 电机驱动初始化 - if (get_reset_cause() == - RCU_FLAG_FWDGTRST) { // 如果是看门狗复位,本次看门狗时间延长并休眠 - printf("continue sleep\r\n"); - watchdog_init(5000); // 看门狗初始化 - prepare_deep_sleep(false); - } User_timer_init(); // 任务Timer初始化 u8 init_status = 0; init_status += KTH7823.init(); // 角度传感器初始化 diff --git a/Robot_V2/sys/config.h b/Robot_V2/sys/config.h index c0c66b4..d0d1fca 100644 --- a/Robot_V2/sys/config.h +++ b/Robot_V2/sys/config.h @@ -5,8 +5,9 @@ // 如果使用CMSIS DSP库,需要定义ARM_MATH_CM4 #define ARM_MATH_CM4 -#define EEP_DEBUG 1 +#define EEP_DEBUG 0 #define OTA_DEBUG 1 +#define SLEEP_DEBUG 1 // Flash分区配置 constexpr u32 BOOTLOADER_SIZE = 0x4000; // 16KB @@ -15,6 +16,11 @@ constexpr u32 APPLATEST_START_ADDR = 0x08011000; // APP起始地址 constexpr u32 APPBACK_START_ADDR = 0x08005000; // APPBACK起始地址 constexpr u32 APP_MAX_SIZE = 0xC000; // 48KB constexpr u32 FMC_PAGE_SIZE = 0x400; // 1KB +constexpr u32 SLEEP_CONFIG_START = 0x0801D400; // 睡眠配置起始地址 +constexpr u32 SLEEP_CONFIG_END = 0x0801D800; // 睡眠配置结束地址 +constexpr u32 FLASH_EMPTY = 0xFFFFFFFF; // 空数据 +constexpr u32 SLEEP_ENABLE = 0x11111111; // 睡眠使能 +constexpr u32 SLEEP_DISABLE = 0x00000000; // 睡眠禁止 // BOOT 相关 constexpr u32 OTA_CONFIG_NUM = 6; diff --git a/Robot_V2/sys/flash.cpp b/Robot_V2/sys/flash.cpp index 5ac1fde..36720a2 100644 --- a/Robot_V2/sys/flash.cpp +++ b/Robot_V2/sys/flash.cpp @@ -4,6 +4,7 @@ #include "config.h" #include "gd32c10x.h" // GD32主头文件,确保外设函数声明可用 #include "gd32c10x_crc.h" +#include "gd32c10x_fmc.h" #include #include #include @@ -13,7 +14,7 @@ extern angle_sensor KTH7823; extern CAN_transmiter CAN0_transmiter; extern FOC_Controlroller FOC; // FOC控制 -inline fmc_state_enum write_ADDRESS(u32 address, u32 data) { +fmc_state_enum write_ADDRESS(u32 address, u32 data) { fmc_unlock(); fmc_flag_clear(FMC_FLAG_PGERR | FMC_FLAG_PGAERR | FMC_FLAG_WPERR); @@ -528,32 +529,40 @@ FlashEepromStatus FlashEEPROM::writeCRC(u32 CRC_NUM, bool saved) { } } +/** + * @brief 设置OTA配置参数到Flash + * + * 此函数用于更新存储在Flash中的OTA相关标志位(如APP可用、启用、校验等)。 + * GD32的Flash特性是:只能将1写为0,不能将0写为1。若要将0变为1,必须先擦除整个页面。 + * 因此,本函数通过读取同一页内的所有参数,擦除该页,然后重新写入有效的参数来实现“写1”操作。 + * + * @param addr: 要设置的Flash地址 + * @param data: 要写入的数据 (通常为OTA_AVAL=0x00000000 或 OTA_NOT_AVAL=0xFFFFFFFF) + * @retval FLASH_EEPROM_OK (0) 表示成功, 其他值表示失败 + */ u8 OTA_SET(u32 addr, u32 data) { fmc_state_enum fmc_status; - u32 temp_val[OTA_CONFIG_NUM]; + u32 temp_val[OTA_CONFIG_NUM]; // 临时数组,用于存储当前所有配置参数的值 volatile u32 *flash_ptr = (volatile u32 *)addr; + + // 1. 检查目标地址的值是否已与期望值相同,如果相同则无需操作 temp_val[0] = *flash_ptr; - if (temp_val[0] == data) { // 如果相同 -#if EEP_DEBUG - printf("OTA_SET%x same%X,skip\r\n", addr, data); -#endif - return FLASH_EEPROM_OK; // 成功 + if (temp_val[0] == data) { + return FLASH_EEPROM_OK; // 值已正确,直接返回成功 } - fmc_unlock(); - // 如果不同,且要写0,直接写 + // 2. 尝试直接写入数据。这适用于将1修改为0的情况(因为Flash允许1->0) if (data == OTA_AVAL) { - fmc_status = write_ADDRESS(addr, OTA_AVAL); - fmc_lock(); + fmc_status = write_ADDRESS(addr, data); + // 如果写入成功,则返回成功 if (fmc_status == FMC_READY) { -#if EEP_DEBUG - printf("OTA_SET%x 0\r\n", addr); -#endif - return FLASH_EEPROM_OK; // 成功可以直接退出 + return FLASH_EEPROM_OK; } + // 如果写入失败(例如地址已被写为0),则继续执行后续的擦除-重写流程 } - // 如果不同或写入出错,要写1,需要重置,然后再写 + // 3. 对于需要将0改回1的操作,必须先擦除整个Flash页 + // 首先读取同一页内所有相关配置参数的当前状态 flash_ptr = (volatile u32 *)n_APP_LATEST_AVAL_ADDR; temp_val[0] = *flash_ptr; flash_ptr = (volatile u32 *)n_APP_BACKUP_AVAL_ADDR; @@ -566,44 +575,50 @@ u8 OTA_SET(u32 addr, u32 data) { temp_val[4] = *flash_ptr; flash_ptr = (volatile u32 *)n_APP_BACKUP_CHECK_ADDR; temp_val[5] = *flash_ptr; + #if EEP_DEBUG - printf("OTA_SET\r\n"); - printf("0x%x_0x%x_0x%x_0x%x\r\n", temp_val[0], temp_val[1], temp_val[2], - temp_val[3]); + printf("OTA_SET - Start updating configuration\r\n"); + // 修复:完整输出所有6个配置项的值,便于调试 + printf("LatestAval:0x%x, BackupAval:0x%x, LatestEn:0x%x, " + "BackupEn:0x%x, LatestCheck:0x%x, BackupCheck:0x%x\r\n", + temp_val[0], temp_val[1], temp_val[2], temp_val[3], temp_val[4], temp_val[5]); #endif - fmc_status = fmc_page_erase(n_APP_LATEST_AVAL_ADDR); // 擦除页 + + // 4. 擦除包含所有配置参数的Flash页 + fmc_status = fmc_page_erase(n_APP_LATEST_AVAL_ADDR); if (fmc_status != FMC_READY) { #if EEP_DEBUG - printf("OTA_SET erase error\r\n"); + printf("OTA_SET - Page erase failed! Status: %d\r\n", fmc_status); #endif - return FLASH_EEPROM_ERASE_ERROR; // 擦除错误退出 + return FLASH_EEPROM_ERASE_ERROR; } + + // 5. 重新写入所有之前为有效(OTA_AVAL)的配置项 + // 注意:不包括当前正在设置的地址,因为它将在最后单独处理 for (int i = 0; i < OTA_CONFIG_NUM; i++) { - if (temp_val[i] == OTA_AVAL and - n_APP_LATEST_AVAL_ADDR + 4 * i != addr) { - fmc_status = write_ADDRESS(n_APP_LATEST_AVAL_ADDR + 4 * i, - OTA_AVAL); // 把需要写入0的写入0 + u32 current_addr = n_APP_LATEST_AVAL_ADDR + 4 * i; + if (temp_val[i] == OTA_AVAL && current_addr != addr) { + fmc_status = write_ADDRESS(current_addr, OTA_AVAL); if (fmc_status != FMC_READY) { - fmc_lock(); #if EEP_DEBUG - printf("OTA_SET write error\r\n"); + printf("OTA_SET - Write backup value failed at 0x%08X!\r\n", current_addr); #endif - return FLASH_EEPROM_WRITE_ERROR; // 写入错误退出 + return FLASH_EEPROM_WRITE_ERROR; } } } - flash_ptr = (volatile u32 *)addr; - temp_val[0] = *flash_ptr; - if (temp_val[0] != data) { + + // 6. 最后,写入本次调用的目标数据 + // 即使addr为0(理论上无效),也检查一下以避免潜在问题 + if (addr != 0) { + fmc_status = write_ADDRESS(addr, data); + if (fmc_status != FMC_READY) { #if EEP_DEBUG - printf("OTA_SET write error\r\n"); + printf("OTA_SET - Final write to target address 0x%08X failed!\r\n", addr); #endif - return FLASH_EEPROM_WRITE_ERROR; + return FLASH_EEPROM_WRITE_ERROR; + } } - fmc_lock(); -#if EEP_DEBUG - printf("OTA_SET%x ok\r\n", addr); -#endif return FLASH_EEPROM_OK; } @@ -613,7 +628,6 @@ u8 OTA_SET(u32 addr, u32 data) { * @retval 无 */ void jumpToBOOT() { - OTA_SET(n_APP_LATEST_EN_ADDR, OTA_NOT_AVAL); // 设置APP为不激活 // 1. 关闭所有中断 __disable_irq(); for (int i = 0; i < 8; i++) { @@ -627,7 +641,7 @@ void jumpToBOOT() { // 4. 关闭一些关键外设时钟 rcu_periph_clock_disable(RCU_GPIOA); - rcu_periph_clock_disable(RCU_GPIOB); + // rcu_periph_clock_disable(RCU_GPIOB); rcu_periph_clock_disable(RCU_GPIOC); rcu_periph_clock_disable(RCU_USART0); rcu_periph_clock_disable(RCU_CAN0); @@ -741,8 +755,10 @@ fmc_state_enum move_app_data() { return (fmc_state_enum)0xFF; // CRC校验失败 } // 设置备份应用可用标志 + fmc_unlock(); OTA_SET(n_APP_BACKUP_AVAL_ADDR, OTA_AVAL); OTA_SET(n_APP_BACKUP_CHECK_ADDR, OTA_AVAL); + fmc_lock(); return fmc_status; } diff --git a/Robot_V2/sys/flash.h b/Robot_V2/sys/flash.h index 77a02dc..4958a8f 100644 --- a/Robot_V2/sys/flash.h +++ b/Robot_V2/sys/flash.h @@ -73,5 +73,6 @@ class FlashEEPROM { void flash_eeprom_example_main(void); u8 OTA_SET(u32 addr, u32 data); fmc_state_enum move_app_data(void); +fmc_state_enum write_ADDRESS(u32 address, u32 data); #endif /* __FLASH_H */ \ No newline at end of file diff --git a/Robot_V2/sys/initial.cpp b/Robot_V2/sys/initial.cpp index 500fca6..e8d62ac 100644 --- a/Robot_V2/sys/initial.cpp +++ b/Robot_V2/sys/initial.cpp @@ -99,8 +99,10 @@ u16 get_reset_cause(void) { void OTA_initial(void) { // 确认当前APP可用 + fmc_unlock(); OTA_SET(n_APP_LATEST_CHECK_ADDR, OTA_AVAL); OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL); + fmc_lock(); u8 transmit_data[2] = {0x0A, 0xAC}; // 确认APP可用 CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_ACK_ID, 2, transmit_data); @@ -112,14 +114,17 @@ void OTA_initial(void) { // 如果BACK不可用 if (back_aval == OTA_NOT_AVAL) { +#if OTA_DEBUG std::printf("notaval\r\n"); +#endif move_app_data(); } else if (back_aval == OTA_AVAL && back_check == OTA_NOT_AVAL) { // BACK已刷写,但未检查 +#if OTA_DEBUG std::printf("aval\r\n"); +#endif move_app_data(); - } - else { + } else { printf("check\r\n"); } -} +} \ No newline at end of file diff --git a/Robot_V2/sys/systick.cpp b/Robot_V2/sys/systick.cpp index 82ff49b..054b2ad 100644 --- a/Robot_V2/sys/systick.cpp +++ b/Robot_V2/sys/systick.cpp @@ -34,10 +34,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "systick.h" #include "Can.h" +#include "config.h" #include "core_cmFunc.h" #include "flash.h" #include "gd32c10x.h" #include "gd32c10x_exti.h" +#include "gd32c10x_fmc.h" #include "initial.h" #include "motor.h" #include @@ -125,58 +127,64 @@ void system_soft_reset(void) { \param[out] none \retval none */ -void prepare_deep_sleep(bool save_data) { +void app_prepare_deep_sleep(bool save_data) { if (save_data) { flashEEP.save_system_config(true); } // 将SIT1044设置为低功耗待机模式 // CAN0_transmiter.CAN_Sleep(); motor_sleep(); + // 在睡眠前调整看门狗超时时间为最大值,使其在唤醒前不会触发 + fwdgt_write_enable(); + fwdgt_config(0xFFF, FWDGT_PSC_DIV256); // 设置最大超时时间 + fwdgt_write_disable(); - // 关闭不必要的外设时钟 - rcu_periph_clock_disable(RCU_TIMER0); // 关闭定时器时钟 - rcu_periph_clock_disable(RCU_TIMER1); - rcu_periph_clock_disable(RCU_SPI0); // 关闭SPI时钟 - rcu_periph_clock_disable(RCU_I2C0); // 关闭I2C时钟 - rcu_periph_clock_disable(RCU_ADC0); // 关闭ADC时钟 - // 注意不要关闭GPIOB和AF的时钟,因为需要它们来处理唤醒中断 - - // 设置深睡眠模式下的电压以进一步降低功耗 - rcu_deepsleep_voltage_set(RCU_DEEPSLEEP_V_0_8); // 设置最低电压 - - // 配置PB8为输入模式,用于外部中断 - rcu_periph_clock_enable(RCU_GPIOB); - rcu_periph_clock_enable(RCU_AF); - gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_8); - - // 配置EXTI中断(连接到PB8引脚) - gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOB, - GPIO_PIN_SOURCE_8); // 配置GPIOB的PIN8作为EXTI8的源 - exti_init(EXTI_8, EXTI_INTERRUPT, EXTI_TRIG_RISING); // 边沿触发 - exti_interrupt_enable(EXTI_8); - nvic_irq_enable(EXTI5_9_IRQn, 0, 0); // - - // 确保中断标志已被清除 - exti_interrupt_flag_clear(EXTI_8); - - watchdog_init(5000); // 看门狗初始化 - // 进入深度睡眠 - pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, WFI_CMD); -} - -/** - * @brief EXTI5_9中断处理函数 - * @param 无 - * @retval 无 - */ -void EXTI5_9_IRQHandler(void) { - if (exti_interrupt_flag_get(EXTI_8) != RESET) { - // 清除中断标志 - exti_interrupt_flag_clear(EXTI_8); - // 禁用EXTI线8的中断,防止重复触发 - exti_interrupt_disable(EXTI_8); - - printf("MCU wake up\r\n"); - system_soft_reset(); + __disable_irq(); + // 遍历最后一个数字,后一个地址写+1 + for (u32 address = SLEEP_CONFIG_END - SIZE_U32; + address >= SLEEP_CONFIG_START; address -= SIZE_U32) { // 遍历睡眠存储区 + volatile u32 *flash_ptr = (volatile u32 *)address; + u32 sleep_enable = *flash_ptr; + if (sleep_enable == FLASH_EMPTY and + address > SLEEP_CONFIG_START) { // 空数据且未到头跳过 + continue; + } else { // 有数据的话读取 +#if SLEEP_DEBUG + printf("app sleep found %08x at %08x\r\n", sleep_enable, address); +#endif + if (address == SLEEP_CONFIG_END - SIZE_U32) { + fmc_unlock(); + fmc_state_enum fmc_status = + fmc_page_erase(SLEEP_CONFIG_START); // 擦除已满的页 + if (fmc_status != FMC_READY) { // 擦除失败报错 + fmc_lock(); + flashEEP.EEPROMStatus = FLASH_EEPROM_ERASE_ERROR; + return; + } + address = SLEEP_CONFIG_START - SIZE_U32; // 重新开始写入 + } + address += SIZE_U32; +#if SLEEP_DEBUG + printf("app sleep prepare write %08x at %08x\r\n", SLEEP_ENABLE, + address); +#endif + fmc_state_enum fmc_status = + write_ADDRESS(address, SLEEP_ENABLE); // 写入数据 +#if SLEEP_DEBUG + volatile u32 *flash_ptr = (volatile u32 *)address; + u32 sleep_check = *flash_ptr; + printf("app sleep wrote %08x at %08x\r\n", sleep_check, address); +#endif + if (fmc_status != FMC_READY) { // 写入失败报错 + flashEEP.EEPROMStatus = FLASH_EEPROM_WRITE_ERROR; +#if SLEEP_DEBUG + printf("app wrote %08x at %08x ERROR\r\n", SLEEP_ENABLE, + address); +#endif + return; + } + break; + } } + jumpToBOOT(); // 跳转到bootloader正式休眠,进入低功耗待机模式 } diff --git a/Robot_V2/sys/systick.h b/Robot_V2/sys/systick.h index 5cf51e5..99dd74c 100644 --- a/Robot_V2/sys/systick.h +++ b/Robot_V2/sys/systick.h @@ -55,7 +55,7 @@ uint8_t get_cpu_usage(void); /* 系统软复位 */ void system_soft_reset(void); /* 准备进入低功耗模式 */ -void prepare_deep_sleep(bool save_data); +void app_prepare_deep_sleep(bool save_data); /* 中断唤醒 */ void EXTI5_9_IRQHandler(void);