#include "../main/SystemInclude.h" /**------------------------------------------------------------------------ * @brief 执行休眠之前,调用此函数选择具体的休眠模式 * @param mode: 休眠模式,可选值: * - PWR_MODE_SLEEP: 睡眠模式(内核停止,外设运行) * - PWR_MODE_DEEP_SLEEP_1: 深度休眠1(时钟停止,不启用低功耗LDO) * - PWR_MODE_DEEP_SLEEP_2: 深度休眠2(时钟停止,启用低功耗LDO) * - PWR_MODE_SNOOZE: ADC小睡模式(ADC独立工作) * - PWR_MODE_SHUTDOWN: 关断模式(数字域掉电) * @note 首先调用 LHL_PWR_SetPowerMode 配置电源模式, * 然后执行 WFI 指令进入休眠。唤醒后自动恢复时钟, * 并调用 ExitLowPowerModeRecoverySysState() 进行唤醒后处理。 * @example EnterLowPowerModeWFI(PWR_MODE_DEEP_SLEEP_2); **/ void EnterLowPowerModeWFI(PWR_POWERMODE_t mode) { delay_ms(800); LHL_PWR_SetPowerMode(mode); LHL_PWR_EnterSleep();/*自带 Restore Clocks */ // 唤醒后从此处继续 → 自动恢复 //重新配置时钟 恢复外设 通知各模块已唤醒 ExitLowPowerModeRecoverySysState(); } /*=============================================================================*/ __LPM_STA_TypeDef lpmState; /**------------------------------------------------------------------------ * @brief 低功耗模式处理函数(由主循环调用) * @note 判断 lpmState.mcuModeSleeping 标志,若为1则进入低功耗流程。 * 先配置 GPIO0.1 为外部中断唤醒源(串口唤醒),再调用用户可重写的 * SetupLowPowertoWakeupByHandwareTrigger() 配置其他硬件触发唤醒源, * 最后根据 lpmState.lpm_mode 选择具体休眠模式并调用 EnterLowPowerModeWFI。 * 唤醒后自动从 WFI 返回,不在此函数内做额外处理。 * @example lpmState.lpm_mode = PWR_MODE_DEEP_SLEEP_2; * lpmState.mcuModeSleeping = 1; * LowPowerModeProcess(); **/ void LowPowerModeProcess(void) { if (lpmState.mcuModeSleeping==0) return ; //判断是否要进入低功耗 //进入低功耗 lpmState.mcuModeSleeping = 0 ; /*使用 UART Rx引脚 外部中断唤醒---------------------------------------------- */ GPIO_EXTI_Init(GPIO0,GPIO_PIN_1,EXTI_LINE_1,EXTI0_1_IRQn);//默认将GPIO0.1设为唤醒源 SetupLowPowertoWakeupByHandwareTrigger();//睡眠前设置。设置中断源唤醒,使用前必须确定该中断可以触发 switch(lpmState.lpm_mode) { case PWR_MODE_DEEP_SLEEP_2 : EnterLowPowerModeWFI(PWR_MODE_DEEP_SLEEP_2); break ; //深度休眠2 case PWR_MODE_SNOOZE : EnterLowPowerModeWFI(PWR_MODE_SNOOZE); break ; //ADC小睡模式 case PWR_MODE_DEEP_SLEEP_1 : EnterLowPowerModeWFI(PWR_MODE_DEEP_SLEEP_1); break ; //深度休眠1 case PWR_MODE_SLEEP : EnterLowPowerModeWFI(PWR_MODE_SLEEP); break ; //普通休眠 case PWR_MODE_SHUTDOWN : EnterLowPowerModeWFI(PWR_MODE_SHUTDOWN); break ; //关断模式 default : EnterLowPowerModeWFI(PWR_MODE_SLEEP); break ; } } // /**------------------------------------------------------------------------ // * @brief 配置SNOOZE模式DMA唤醒源 // * @param ch: 0=使用ADC0的DMA通道唤醒, 1=使用ADC1的DMA通道唤醒 // * @note 寄存器定义: SNOOZE_SRC_EXIT [2] // * 0: 使用搬运ADC0的DMA通道中断作为唤醒core的源 // * 1: 使用搬运ADC1的DMA通道中断作为唤醒core的源 //**/ //void ADC_Set_SNOOZE_DMA_Wakeup(ADC_ID_t ADCx ) //{ // uint32_t reg_val = pADC->DMA_MODE; // //// LHL_ADC_DMACmd(ADCx, ENABLE); //dma_adc 初始化已经使能 // reg_val &= ~ADC_SUBSYS_USER_DMA_MODE_SNOOZE_EXIT_SRC_Msk; // 清bit2 // reg_val |= (((uint32_t)ADCx - 1)<< ADC_SUBSYS_USER_DMA_MODE_SNOOZE_EXIT_SRC_Pos); // pADC->DMA_MODE = reg_val; //} /**------------------------------------------------------------------------ * @brief 进入低功耗前的硬件触发唤醒源配置(弱函数,可重写) * @note 该函数为弱定义,用户可在应用程序中重新实现以自定义唤醒源。 * 默认实现中配置了: * - LPTIM2 启动(用于触发 ADC,但未连接 XLINK) * - RTC 闹钟设置为 1 秒后触发,并连接 XLINK 到 ADC 触发 * - ADC 初始化为硬件触发单次转换模式 * - 使能 ADC 的 EXTI 线(EXTI_LINE_12)作为唤醒源 * @example 无(由系统调用) **/ __weak void SetupLowPowertoWakeupByHandwareTrigger(void) { /*ADC唤醒-----SNOOZE模式有效================================================*/ /* 1. LPTIM 设置 */ StartLPTimer2(32768); //设置LPTimer触发adc转换周期 = 4s 16384 65535 启动需要时间,先进行,否则首次触发会失败 // /* 2. 配置XLINK,连接LPTIM到ADC硬件触发 */ // XLINK_LPTIM_To_ADC_Trigger(LPTIM2, ADC_0); // /* 3. 设置 LPTIM2作为ADC硬件触发源*/ // MODIFY_REG(pPWR->CR, PWR_CR_ADC_MSYS_TSEL_Msk, (3U << PWR_CR_ADC_MSYS_TSEL_Pos)); /* 1. RTC设置 */ StartRTC_AlarmAfterSeconds(1);//设置 N 秒后闹钟 LHL_RTC_ITConfig(RTC_IT_ALRIE, DISABLE);//禁用RTC闹钟中断 (使能会自动配置 EXTI_LINE_9 作为唤醒源) // 2. 设置 RTC ALARM作为ADC硬件触发源 MODIFY_REG(pPWR->CR, PWR_CR_ADC_MSYS_TSEL_Msk, (0U << PWR_CR_ADC_MSYS_TSEL_Pos)); /* 3. 配置XLINK,连接RTC ALARM到ADC硬件触发 */ XLINK_RTC_ALARM_To_ADC_Trigger(ADC_0); /* 4. ADC 设置 :硬件触发+单次转换 */ ADC_REF_Init(REF_INTERNAL_2P5V, REF_INTERNAL_2P5V); ADC_Init_For_LowerPower(ADC_0, ADC_SPS_5, GAIN32, ADC0_AIN0, ADC0_AIN1); /* 5. 设置唤醒源 */ // LPTIM_EXTI_Init(LPTIM2);//将LPTimer2设为唤醒源 EXTI_LINE_11 ADC_EXTI_Init();//将ADC设为唤醒源 EXTI_LINE_12 } /**------------------------------------------------------------------------ * @brief 退出低功耗后的系统恢复处理(弱函数,可重写) * @note 该函数为弱定义,用户可重写以定制唤醒后的操作。 * 默认实现中: * - 清除 EXTI 中断标志位 * - 恢复 UART 功能(重新初始化并启动 DMA 接收) * - 读取一次 ADC 值并打印唤醒次数、唤醒源和 ADC 值 * - 默认设置下次仍进入 SNOOZE 模式(lpmState 相应设置) * @example 无(由 EnterLowPowerModeWFI 自动调用) **/ __weak void ExitLowPowerModeRecoverySysState(void) { lpmState.mcuModeSleeping = 0 ;//唤醒mcu并将标志位复位 static u16 wakeupcount = 0 ,wakeupsource; wakeupcount++ ; wakeupsource = pEXTI->PR ;//中断唤醒源标志 pEXTI->EMR //事件唤醒源标志位 pEXTI->PR = 0xFFFF;//清除唤醒源标志位 /* 唤醒后处理 */ //1. UART---------------------------------------------------- NVIC_DisableIRQ(EXTI0_1_IRQn); //须禁用uart rx的外部中断 MainUartInit(baudRateVaule[baudRate]);//将引脚设置回串口功能 StartDMAForRxdMainUartData();//开始串口DMA传输 // TEST_PIN_TOGGLE(); //2. ADC---------------------------------------------------- //判断阈值等---- u16 adc_buffer = ADC_ReadData(ADC_0);//直接读取不使用中断 // if(adc_buffer > 42768 ) lpmState.mcuModeSleeping = 0 ;//退出睡眠 lpmState.lpm_mode = PWR_MODE_SNOOZE ; lpmState.mcuModeSleeping = 1 ;//默认再次睡眠 printf(" \n ---唤醒次数 = %d ; 唤醒源 = %d 当前ADC值 = %d \n" ,wakeupcount , wakeupsource ,adc_buffer); }