py32duino / Arduino-PY32

Arduino core for the PY32.
MIT License
43 stars 8 forks source link

Deep sleep and external wakeup #8

Closed garudaonekh closed 9 months ago

garudaonekh commented 10 months ago

您的功能请求是否与解决某些问题有关?请描述一下。/ Is your feature request related to a problem? Please describe.

Hi, I need to wakeup the MCU when there's an incoming LORA package which is triggered on a PIN. Thus I need to attach interrupt to that pin and after handling the incoming package, the MCU will go to deepsleep and stop mode.

描述您想要的解决方案 / Describe the solution you'd like

Pin Interrupt to wakeup from deepsleep+stop mode. Or at least enter deep sleep.

描述您想要的详细使用步骤描述 / Describe the solution you'd like to use in what way

I see there's a header file which may help with my deep sleep but I don't know how to use this one yet. #include "py32yyxx_ll_pwr.h"

其他备注信息或截图 / Add any other context or screenshots about the feature request here

No response

确认信息

HalfSweet commented 10 months ago

For the Arduino compatibility layer, there is no native API provided.

You can use HAL's API directly for this.

For the Arduino compatibility layer operations may take some time to be possible to add, the problem arises when we don't know how to define it perfectly :(

garudaonekh commented 10 months ago

include "py32yyxx_ll_pwr.h"

I think this library is the way to go py32yyxx_ll_pwr.h, isn't it?

garudaonekh commented 10 months ago

For the Arduino compatibility layer, there is no native API provided.

You can use HAL's API directly for this.

For the Arduino compatibility layer operations may take some time to be possible to add, the problem arises when we don't know how to define it perfectly :(

With a few more imports, I can get this work https://github.com/IOsetting/py32f0-template/tree/main/Examples/PY32F0xx/LL/LPTIM/LPTIM1_Wakeup with Arduino

But the current consumption is still very high 5ma.

HalfSweet commented 10 months ago

5mA? That shouldn't be the case, normal operation doesn't draw more than 3mA, maybe you have some leakage in the GPIOs to cause this

garudaonekh commented 10 months ago

5mA? That shouldn't be the case, normal operation doesn't draw more than 3mA, maybe you have some leakage in the GPIOs to cause this

You are right. But somehow, if I run a normal code without sleep, it also consume around 5ma thus I can assume that my deep sleep, not working and does nothing

HalfSweet commented 10 months ago

Can you show me how you do it?

garudaonekh commented 10 months ago

I use PY32F030


#include "py32f0xx_ll_bus.h"
#include "py32f0xx_hal_dma.h"
#include "py32f0xx_hal_uart.h"
#include "py32f0xx_ll_cortex.h"
#include "py32f0xx_ll_lptim.h"
#include "py32f0xx_ll_pwr.h"
#include "py32f0xx_ll_rcc.h"
#include "py32f0xx_ll_system.h"
#include "py32f0xx_hal_rcc.h"
#include "py32f0xx_ll_utils.h"
#include "Arduino.h"

static void APP_GPIO_Config(void);
static void APP_ConfigLPTIMOneShot(void);

void BSP_RCC_HSI_24MConfig(void)
{
  LL_RCC_HSI_Enable();
  LL_RCC_HSI_SetCalibFreq(LL_RCC_HSICALIBRATION_24MHz);
  while(LL_RCC_HSI_IsReady() != 1);

  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);

  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSISYS);
  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSISYS);

  LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);

  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
  // Update global SystemCoreClock(or through SystemCoreClockUpdate function) 
  LL_SetSystemCoreClock(24000000);
  // Re-init frequency of SysTick source 
  LL_Init1msTick(24000000);
}
void setup(void)
{
  Serial.begin(115200);
  // Set HSI 24MHz as system clock source
  delay(100);
  Serial.printf("PY32F0 LPTIM Wakeup Demo\r\nClock: %ld\r\n", SystemCoreClock);
   Serial.printf("Entering stop mode ...");
  LL_mDelay(10);

  delay(100);
  BSP_RCC_HSI_24MConfig();
  // Initialize UART on PA2:TX PA3:RX

  APP_GPIO_Config();

  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LPTIM1);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

  APP_ConfigLPTIMOneShot();

  while (1)
  {
    LL_PWR_EnableLowPowerRunMode();
    LL_LPTIM_Disable(LPTIM1);
    LL_LPTIM_Enable(LPTIM1);
    //delay(10);
    LL_mDelay(10);
    LL_LPTIM_StartCounter(LPTIM1, LL_LPTIM_OPERATING_MODE_ONESHOT);LL_mDelay(10);
    Serial.printf("Entering stop mode ...");
    LL_mDelay(10);
    LL_LPM_EnableDeepSleep();
    __WFI();
    Serial.printf("wakeup\r\n");
  }

}

static void APP_GPIO_Config(void)
{
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
  LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_6, LL_GPIO_MODE_OUTPUT);
}

static void APP_ConfigLPTIMOneShot(void)
{
  LL_RCC_LSI_Enable();
  while(LL_RCC_LSI_IsReady() == 0);
  // Set LSI as LPTIM1 clock source
  LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM1_CLKSOURCE_LSI);
  // Prescaler = 64
  LL_LPTIM_SetPrescaler(LPTIM1, LL_LPTIM_PRESCALER_DIV128);
  LL_LPTIM_SetUpdateMode(LPTIM1, LL_LPTIM_UPDATE_MODE_ENDOFPERIOD);
  LL_LPTIM_EnableIT_ARRM(LPTIM1);
  LL_LPTIM_Enable(LPTIM1);

  // 32768 / 128 = 256 
  LL_LPTIM_SetAutoReload(LPTIM1, 255);

  NVIC_EnableIRQ(LPTIM1_IRQn);
  NVIC_SetPriority(LPTIM1_IRQn, 0);
}

void LPTIM1_IRQHandler(void)
{
  if (LL_LPTIM_IsActiveFlag_ARRM(LPTIM))
  {
    LL_LPTIM_ClearFLAG_ARRM(LPTIM);
    LL_GPIO_TogglePin(GPIOB, LL_GPIO_PIN_6);
    LL_LPTIM_StartCounter(LPTIM1, LL_LPTIM_OPERATING_MODE_ONESHOT);
  }
}

void loop() {
}
HalfSweet commented 10 months ago

You can refer to https://github.com/OpenPuya/PY32F0xx_Firmware/tree/master/Projects/PY32F030-STK/Example_LL/LPTIM/LPTIM_WakeUp Another thing to care about is that inside arduino you are actually using C++, which is not compatible with C's ABI. Therefore interrupt Handler functions such as LPTIM1_IRQHandler should be modified with extern "C".

Also, you should configure NVIC before using _IT functions

garudaonekh commented 10 months ago

LPTIM1_IRQHandler

Still the current consumption doesn't change. However, I remark APP_SystemClockConfig() because the stop working in this function. However, I use HSI 8MHZ in Arduino Clock Source Config.

#include "py32f0xx_hal_dma.h"
#include "py32f0xx_hal_uart.h"
#include "py32f0xx_hal_rcc.h"
/* Includes ------------------------------------------------------------------*/
#include "py32f0xx_ll_rcc.h"
#include "py32f0xx_ll_bus.h"
#include "py32f0xx_ll_system.h"
#include "py32f0xx_ll_exti.h"
#include "py32f0xx_ll_cortex.h"
#include "py32f0xx_ll_utils.h"
#include "py32f0xx_ll_pwr.h"
#include "py32f0xx_ll_dma.h"
#include "py32f0xx_ll_gpio.h"
#include "py32f0xx_ll_lptim.h"
#include <Arduino.h>
#if defined(USE_FULL_ASSERT)
#include "py32_assert.h"
#endif /* USE_FULL_ASSERT */

/* Private includes ----------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions prototypes ---------------------------------------------*/
void APP_ErrorHandler(void);
void APP_LPTIMCallback(void);

/* Private defines -----------------------------------------------------------*/

//#include "py32f030xx_ll_Start_Kit.h"

/* Private define ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private user code ---------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
static void APP_SystemClockConfig(void);
static void APP_ConfigLPTIMOneShot(void);
static void APP_uDelay(uint32_t Delay);
static void APP_LPTIMClockconf(void);

/**
  * @brief  应用程序入口函数.
  * @retval int
  */
void setup(void)
{  
  Serial.begin(115200);
  /* 配置系统时钟 */
  delay(100);
 // APP_SystemClockConfig(); -->Remark this otherwwise, it stop working here
  Serial.println("HELLO LPTIM");
  /* 使能LPTIM、PWR时钟 */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LPTIM1);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

  /* 初始化LED、按键 */  
  //digitalWrite(5,HIGH);
 // BSP_LED_Init(LED3);
 // BSP_PB_Init(BUTTON_USER,BUTTON_MODE_GPIO);

  /* 配置LPTIM时钟源为LSI */
  APP_LPTIMClockconf();

  /* 配置并使能LPTIM */
  APP_ConfigLPTIMOneShot();
  /* 点亮LED */
  //BSP_LED_On(LED3);

  /* 等待按键按下 */
  //while(BSP_PB_GetState(BUTTON_USER) != 0)
  //{}

  /* 关闭LED */
  delay(100);
  //BSP_LED_Off(LED3);
  //digitalWrite(5,LOW);
  Serial.println("End of setup");

}
void loop(){
    /* 使能低功耗运行模式 */
    LL_PWR_EnableLowPowerRunMode();
    /* 失能LPTIM */
    LL_LPTIM_Disable(LPTIM1);
    /* 使能LPTIM */
    LL_LPTIM_Enable(LPTIM1);
    Serial.println("Entering sleep");
    delay(100);
    /* 延时65us */
    APP_uDelay(65);

    /* 开启单次模式 */
    LL_LPTIM_StartCounter(LPTIM1,LL_LPTIM_OPERATING_MODE_ONESHOT);

    /* 使能STOP模式 */
    LL_LPM_EnableDeepSleep();

    /* 进入STOP模式,等待中断唤醒 */
    __WFI();

    Serial.println("Wake up");

}

/**
  * @brief  LPTIM 时钟配置
  * @param  无
  * @retval 无
  */
static void APP_LPTIMClockconf(void)
{
  /* 开启LSI */
  LL_RCC_LSI_Enable();

  /* 等待LSI就绪 */
  while(LL_RCC_LSI_IsReady() == 0)
  {}

  /* 配置LSI为LPTIM时钟源 */
  LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM1_CLKSOURCE_LSI);
}

/**
  * @brief  配置LPTIM单次模式
  * @param  无
  * @retval 无
  */
static void APP_ConfigLPTIMOneShot(void)
{
  /* 配置LPTIM */
  /* LPTIM预分频器128分频 */
  LL_LPTIM_SetPrescaler(LPTIM1,LL_LPTIM_PRESCALER_DIV128);

  /* LPTIM计数周期结束更新ARR */
  LL_LPTIM_SetUpdateMode(LPTIM1,LL_LPTIM_UPDATE_MODE_ENDOFPERIOD);

  /* 使能ARR中断 */
  LL_LPTIM_EnableIT_ARRM(LPTIM1);

  /* 使能NVIC请求 */
  NVIC_EnableIRQ(LPTIM1_IRQn);
  NVIC_SetPriority(LPTIM1_IRQn,0);

  /* 使能LPTIM */
  LL_LPTIM_Enable(LPTIM1);

  /* 配置重装载值:51 */
  LL_LPTIM_SetAutoReload(LPTIM1,51);
}

/**
  * @brief  LPTIM ARR中断回调函数
  * @param  无
  * @retval 无
  */
void APP_LPTIMCallback(void)
{
  /*翻转LED*/
  //BSP_LED_Toggle(LED3);
}

/**
  * @brief  微秒延时函数
  * @param  Delay;延时值
  * @retval 无
  */
static void APP_uDelay(uint32_t Delay)
{
  uint32_t temp;
  SysTick->LOAD=Delay*(SystemCoreClock/1000000);
  SysTick->VAL=0x00;
  SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;
  do
  {
    temp=SysTick->CTRL;
  }
  while((temp&0x01)&&!(temp&(1<<16)));
  SysTick->CTRL=0x00;
  SysTick->VAL =0x00;
}

/**
  * @brief  系统时钟配置函数
  * @param  无
  * @retval 无
  */
static void APP_SystemClockConfig(void)
{
  /* 使能HSI */
  LL_RCC_HSI_Enable();
  while(LL_RCC_HSI_IsReady() != 1)
  {
  }

  /* 设置 AHB 分频 */
  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);

  /* 配置HSISYS作为系统时钟源 */
  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSISYS);
  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSISYS)
  {
  }

  /* 设置 APB1 分频 */
  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
  LL_Init1msTick(8000000);

  /* 更新系统时钟全局变量SystemCoreClock(也可以通过调用SystemCoreClockUpdate函数更新) */
  LL_SetSystemCoreClock(8000000);
}

/**
  * @brief  错误执行函数
  * @param  无
  * @retval 无
  */
void APP_ErrorHandler(void)
{
  /* 无限循环 */
  while(1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  输出产生断言错误的源文件名及行号
  * @param  file:源文件名指针
  * @param  line:发生断言错误的行号
  * @retval 无
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* 用户可以根据需要添加自己的打印信息,
     例如: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* 无限循环 */
  while (1)
  {
  }
}
#endif /* USE_FULL_ASSERT */
garudaonekh commented 9 months ago

Ok, I use the HAL port which is now using only 6ua. Thanks.