基于ST官方的Nucleo_L073RZ评估板低功耗测试(程序基于LL库)

       手头有一块ST官方的Nucleo_L073RZ评估板,想看看这个L0系列进入停止模式功耗能做到多少,经过周末一天的写程序测试,从测试结果来看进入停止模式后功耗最低能降到1ua左右,还是不错的。后面在低功耗的基础上增加了串口打印,RTC定时唤醒功能,以及ADC采样功能(软件触发),都能使功耗稳定在2ua左右。

      下面分享一下我的部分代码供需要调试低功耗应用的人一些参考,一些关键的地方在代码中强调出来了。要降功耗主要注意以下几点:

1.GPIO的配置(不用的引脚配置为模拟输入,使用到的引脚应保证低功耗状态时没有压降产生,即需要明确GPIO默认状态是高还是低)

2.要想达到极低功耗需要使内部调压器处在低功耗模式

3.如果使用了内部ADC通道例如VREFINT, 内部温度传感器,需要记得在进入低功耗模式时关闭(参考我的代码)

4.使用了ADC情况下,进入低功耗模式时记得关闭ADC的调压器以及关闭ADC外设(参考我的代码)

 

参考代码链接:https://download.csdn.net/download/qq_27718231/12918647


/*
 * 函数名称: NUCLEO_GPIO_Init
 * 函数说明: 初始化评估板的GPIO
 * 输入参数: 无
 * 返回参数: 无
 */
void NUCLEO_GPIO_Init(void)
{
  LL_GPIO_InitTypeDef GPIO_InitStruct={0};
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);
  
	GPIO_InitStruct.Pin=LL_GPIO_PIN_0|LL_GPIO_PIN_1|LL_GPIO_PIN_2|LL_GPIO_PIN_3
										 |LL_GPIO_PIN_4|LL_GPIO_PIN_5|LL_GPIO_PIN_6|LL_GPIO_PIN_7
	                   |LL_GPIO_PIN_8|LL_GPIO_PIN_9|LL_GPIO_PIN_10|LL_GPIO_PIN_11
										 |LL_GPIO_PIN_12|LL_GPIO_PIN_13|LL_GPIO_PIN_14|LL_GPIO_PIN_15;
	GPIO_InitStruct.Mode=LL_GPIO_MODE_ANALOG;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
	LL_GPIO_Init(GPIOA, &GPIO_InitStruct);  

	GPIO_InitStruct.Pin=LL_GPIO_PIN_0|LL_GPIO_PIN_1|LL_GPIO_PIN_2|LL_GPIO_PIN_3
										 |LL_GPIO_PIN_4|LL_GPIO_PIN_5|LL_GPIO_PIN_6|LL_GPIO_PIN_7
	                   |LL_GPIO_PIN_8|LL_GPIO_PIN_9|LL_GPIO_PIN_10|LL_GPIO_PIN_11
										 |LL_GPIO_PIN_12|LL_GPIO_PIN_13|LL_GPIO_PIN_14|LL_GPIO_PIN_15;
	GPIO_InitStruct.Mode=LL_GPIO_MODE_ANALOG;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
	LL_GPIO_Init(GPIOB, &GPIO_InitStruct);


	GPIO_InitStruct.Pin=LL_GPIO_PIN_0|LL_GPIO_PIN_1|LL_GPIO_PIN_2|LL_GPIO_PIN_3
										 |LL_GPIO_PIN_4|LL_GPIO_PIN_5|LL_GPIO_PIN_6|LL_GPIO_PIN_7
	                   |LL_GPIO_PIN_8|LL_GPIO_PIN_9|LL_GPIO_PIN_10|LL_GPIO_PIN_11
										 |LL_GPIO_PIN_12|LL_GPIO_PIN_13;
	GPIO_InitStruct.Mode=LL_GPIO_MODE_ANALOG;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
	LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  
  /* PA5-LED2 */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_5;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);  
	
	
	
}

/*
 * 函数名称: Enter_DeepSleep
 * 函数说明: 进入深度睡眠模式
 * 输入参数: 无
 * 返回参数: 无
 */
static void Enter_DeepSleep(void)
{
	uint32_t tmpreg = 0U;
  /* Clear all exti interrupt flag */
  NVIC_DisableIRQ(SysTick_IRQn);	
  EXTI->PR = 0x007DFFFF;
  LL_RTC_ClearFlag_WUT(RTC);   
  
	/* 使能超低功耗模式,此模式下会关闭VREFINT (下面这两句对降功耗有帮助)*/
	LL_PWR_EnableUltraLowPower();
  /* 使能快速唤醒 */
	LL_PWR_EnableFastWakeUp();
  
  /* 调压器模式:低功耗 (下面这一句对降功耗有帮助)*/
	LL_PWR_SetRegulModeLP(LL_PWR_REGU_LPMODES_LOW_POWER);
  
  /* Set STOP2 mode when CPU enters deepsleep */
  LL_PWR_SetPowerMode(LL_PWR_MODE_STOP);
	
  /* Set SLEEPDEEP bit of Cortex System Control Register */
  LL_LPM_EnableDeepSleep();  

  /* Request Wait For Interrupt */
  __WFI();  
  
  /* Reset SLEEPDEEP bit of Cortex System Control Register */
  SCB->SCR &= (uint32_t)~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
  /* 从停止模式唤醒后需要重新初始化时钟 */
  SystemClock_Config();
}


/*
 * 函数名称: Adc1_CH_Init
 * 函数说明: ADC通道配置
 * 输入参数: 无
 * 返回参数: 无
 */
void Adc_CH_Init(void)
{
  LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
  LL_ADC_InitTypeDef ADC_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC1);
  
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);
  /**ADC GPIO Configuration  
  PC4   ------> ADC_IN14
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /** 配置外部通道 
  */
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_14);
  /** 配置内部通道VREFINT
  */
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_VREFINT);
//  /* 使能VREFINT(注只在需要使用内部通道VREFINT时才需要操作) */
//  LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_VREFINT);
  /** Common config 
  */
  ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
  ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
  ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
  ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_NONE;
  ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
  
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_39CYCLES_5);
  LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
  LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD);
  LL_ADC_SetCommonFrequencyMode(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_CLOCK_FREQ_MODE_HIGH);
  LL_ADC_DisableIT_EOC(ADC1);
  LL_ADC_DisableIT_EOS(ADC1);
  
  ADC_InitStruct.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV2; /* PCLK=16MHz */
  ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;
  ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
  ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
  LL_ADC_Init(ADC1, &ADC_InitStruct);

//  LL_ADC_DeInit(ADC1);
}

/*
 * 函数名称: Start_Adc
 * 函数说明: 使能ADC转换
 * 输入参数: 无
 * 返回参数: 无
 */
void Start_Adc(void)
{
  uint32_t temp_buff=0, vref_buff=0;
  uint32_t wait_loop_index;
  /* 开启ADC调压器 */
  LL_ADC_EnableInternalRegulator(ADC1);  
  /* 等待ADC稳压器稳定 */
  wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
  while(wait_loop_index != 0)
  {
    wait_loop_index--;
  }  	  
  /* 校准ADC */
  LL_ADC_StartCalibration(ADC1); 
  /* 等待校准完成 */
  while (LL_ADC_IsCalibrationOnGoing(ADC1) != 0)
  {
  }
  /* 校准完成之后需要等待一段时间才能使能ADC */
  wait_loop_index = ((LL_ADC_DELAY_CALIB_ENABLE_ADC_CYCLES * 32) >> 1);
  while(wait_loop_index != 0)
  {
    wait_loop_index--;
  }
  /* 使能之前清除ADRDY标志 */
  LL_ADC_ClearFlag_ADRDY(ADC1);
  /* 使能ADC */
  LL_ADC_Enable(ADC1);
  /* 等待ADC使能 */
	while(LL_ADC_IsActiveFlag_ADRDY(ADC1) != SET)
  {
  }  

  /* 使能VREFINT(注只在需要使用内部通道VREFINT时才需要操作) */
  LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_VREFINT);  
  /* 开始转换(软件触发转换16次) */  
  for(uint8_t i=0; i<16; i++)
  {
    LL_ADC_REG_StartConversion(ADC1);
    while(LL_ADC_IsActiveFlag_EOC(ADC1) != SET);
    temp_buff += LL_ADC_REG_ReadConversionData12(ADC1);
    LL_ADC_REG_StartConversion(ADC1);
    while(LL_ADC_IsActiveFlag_EOC(ADC1) != SET);
    vref_buff += LL_ADC_REG_ReadConversionData12(ADC1); 
  }
  /* 求16次转换平均值 */
  temp_buff >>= 4;
  vref_buff >>= 4;
  printf("Ch14=%d, Vrefint=%d\n", temp_buff, vref_buff);
  /* 根据采样的内部参考值计算VDDA的值 */
  printf("Vdda=%d\n", 3000*(*VREFINT_CAL)/vref_buff);
}


/*
 * 函数名称: Start_Adc
 * 函数说明: 停止ADC转换
 * 输入参数: 无
 * 返回参数: 无
 */
void Stop_Adc(void)
{
  LL_ADC_REG_StopConversion(ADC1);
  while(LL_ADC_REG_IsStopConversionOngoing(ADC1) != RESET)
  {
  }
  LL_ADC_Disable(ADC1);
  /* 失能VREFINT,降低功耗(注只在需要使用内部通道VREFINT时才需要操作) */
  LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_NONE); 
}

       下面是我的测试结果:

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值