Files
CHJ/user/MCU/lhl_lowpower.c
2026-03-20 21:19:53 +08:00

172 lines
6.6 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
}