/** **************************************************************************************** * @brief Set sleep timer wakeup * @param[in] clk_src 32KHz clock source * @description * This function is used to set MCU wakeup by sleep timer. ***************************************************************************************** */ void wakeup_by_sleep_timer(int clk_src) { uint32_t dis_type, en_type; uint32_t pd_type; // Enable sleep timer wakeup syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_OSC_WAKEUP_EN, MASK_ENABLE); sleep_env.wakeup_by_sleeptimer = 1; if (clk_src == RCO_32K) { // Enable 32k RCO dis_type = SYSCON_MASK_DIS_RCO; en_type = MASK_ENABLE; pd_type = SYSCON_MASK_PD_XTAL32; sleep_env.retention_modules |= SYSCON_MASK_DIS_RCO; } else { // Enable 32k XTAL dis_type = SYSCON_MASK_DIS_XTAL32; en_type = MASK_DISABLE; pd_type = SYSCON_MASK_PD_RCO; sleep_env.retention_modules |= SYSCON_MASK_DIS_XTAL32; } syscon_SetPGCR1WithMask(QN_SYSCON, dis_type, MASK_DISABLE); syscon_SetCMDCRWithMask(QN_SYSCON, SYSCON_MASK_SEL_CLK_32K, en_type); syscon_SetPGCR0WithMask(QN_SYSCON, SYSCON_MASK_PD_XTAL32|SYSCON_MASK_PD_RCO, pd_type); }
/** **************************************************************************************** * @brief Restore from low power mode * @param[in] callback callback before XTAL clock ready * @description * This function is used to set MCU restoring from low power mode, switch system clock to XTAL. ***************************************************************************************** */ void restore_from_low_power_mode(void (*callback)(void)) { syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_PD_STATE|SYSCON_MASK_DVDD12_PMU_SET, MASK_DISABLE); if (callback != NULL) { callback(); } // 16MHz/32MHz XTAL is ready while (!(syscon_GetBLESR(QN_SYSCON) & SYSCON_MASK_CLK_RDY)) { // XTAL shall be ready before BLE wakeup if (check_ble_wakeup()) { // In this case XTAL wakeup duration is larger than setting. // The parameter 'Oscillator wake-up time' in the NVDS should be revised. #if (QN_DBG_INFO) set_dbg_info(QN_DBG_INFO_XTAL_WAKEUP_DURATION); #endif } } //syscon_SetCMDCRWithMask(QN_SYSCON, SYSCON_MASK_CLK_MUX, CLK_XTAL<<SYSCON_POS_CLK_MUX); syscon_set_sysclk_src(CLK_XTAL, __XTAL); syscon_set_ahb_clk(__AHB_CLK); syscon_set_apb_clk(__APB_CLK); }
/** **************************************************************************************** * @brief Power off serial flash. ***************************************************************************************** */ void power_off_flash(void) { // wait for the flash is free when flash is entered power-down while (is_flash_busy()); // power off syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_FLASH_VCC_EN, MASK_DISABLE); // flash clock off flash_clock_off(); }
/** **************************************************************************************** * @brief Power on serial flash. ***************************************************************************************** */ void power_on_flash(void) { // power on syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_FLASH_VCC_EN, MASK_ENABLE); // wait for 200us then enable flash clock delay(g_AhbClock / 45000 + 1); // flash clock on flash_clock_on(); // wait for flash is ready while(!is_flash_present()); }
/** **************************************************************************************** * @brief Set analog comparator wakeup * @param[in] acmpch enum ACMP_CH * @param[in] callback Callback function pointer, which is called in IRQHandler. * @description * This function is used to set MCU wakeup by analog comparator. ***************************************************************************************** */ void wakeup_by_analog_comparator(enum ACMP_CH acmpch, void (*callback)(void)) { if (sleep_env.wakeup_by_sleeptimer == 0) { // Disable sleep timer wakeup syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_OSC_WAKEUP_EN, MASK_DISABLE); } if (acmpch == ACMP0) { analog_pin_enable(AIN0, MASK_ENABLE); } else { analog_pin_enable(AIN2, MASK_ENABLE); } comparator_init(acmpch, VDD_8, ACMPO_0_GEN_INT, callback); }
/** **************************************************************************************** * @brief Set GPIO wakeup * @param[in] pin wakeup pin: P0 and P1 * @param[in] type Wakeup type: high, low, change * @description * This function is used to set MCU wakeup by gpio pin. ***************************************************************************************** */ void wakeup_by_gpio(enum gpio_pin pin, enum gpio_wakeup_type type) { if (sleep_env.wakeup_by_sleeptimer == 0) { // Disable sleep timer wakeup syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_OSC_WAKEUP_EN, MASK_DISABLE); } // configure gpio wakeup pin gpio_wakeup_config(pin, type); gpio_enable_interrupt(pin); // Ensure gpio interrupt is not pending before the test NVIC_ClearPendingIRQ(GPIO_IRQn); // Enable Interrupts NVIC_EnableIRQ(GPIO_IRQn); }
/** **************************************************************************************** * @brief Enter low power mode * @param[in] en enabled peripheral at low power mode * @description * This function is used to set MCU entering into low power mode. ***************************************************************************************** */ void enter_low_power_mode(uint32_t en) { PGCR1_restore = syscon_GetPGCR1(QN_SYSCON); syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_PD_STATE|SYSCON_MASK_PMUENABLE, MASK_DISABLE); low_power_mode_en = 1; // set system clock to 32K syscon_set_sysclk_src(CLK_LOW_32K, __32K_TYPE); syscon_SetCMDCRWithMask(QN_SYSCON, SYSCON_MASK_AHB_DIV_BYPASS|SYSCON_MASK_APB_DIV_BYPASS, MASK_ENABLE); uint32_t mask; // power off all not needed modules mask = SYSCON_MASK_DIS_OSC | SYSCON_MASK_DIS_BG | SYSCON_MASK_DIS_V2I | SYSCON_MASK_DIS_BUCK | SYSCON_MASK_DIS_VREG_A | SYSCON_MASK_DIS_VREG_D | SYSCON_MASK_DIS_XTAL #if QN_32K_RCO == TRUE | SYSCON_MASK_DIS_XTAL32 #endif | SYSCON_MASK_DIS_REF_PLL | SYSCON_MASK_DIS_LO_VCO | SYSCON_MASK_DIS_LO_PLL | SYSCON_MASK_DIS_PA | SYSCON_MASK_DIS_LNA | SYSCON_MASK_DIS_LNA_PKDET | SYSCON_MASK_DIS_MIXER | SYSCON_MASK_DIS_PPF_PKDET | SYSCON_MASK_DIS_PPF | SYSCON_MASK_DIS_RX_PKDET | SYSCON_MASK_DIS_RX_ADC | SYSCON_MASK_DIS_SAR_ADC #if (QN_32K_RCO == FALSE) | SYSCON_MASK_DIS_RCO #endif | SYSCON_MASK_DIS_MEM7 | SYSCON_MASK_DIS_MEM6 | SYSCON_MASK_DIS_MEM5 | SYSCON_MASK_DIS_MEM4 | SYSCON_MASK_DIS_MEM3 | SYSCON_MASK_DIS_MEM2 | SYSCON_MASK_DIS_MEM1 | SYSCON_MASK_DIS_SAR_BUF ; syscon_SetPGCR1WithMask(QN_SYSCON, mask&(~QN_MEM_RETENTION), MASK_ENABLE); #if 0 // gating all not needed modules mask = SYSCON_MASK_GATING_TIMER3 | SYSCON_MASK_GATING_TIMER2 #if QN_32K_RCO == FALSE | SYSCON_MASK_GATING_TIMER1 #endif | SYSCON_MASK_GATING_TIMER0 | SYSCON_MASK_GATING_UART1 | SYSCON_MASK_GATING_UART0 | SYSCON_MASK_GATING_SPI1 | SYSCON_MASK_GATING_SPI0 //| SYSCON_MASK_GATING_32K_CLK | SYSCON_MASK_GATING_SPI_AHB //| SYSCON_MASK_GATING_GPIO | SYSCON_MASK_GATING_ADC | SYSCON_MASK_GATING_DMA //| SYSCON_MASK_GATING_BLE_AHB | SYSCON_MASK_GATING_PWM ; syscon_SetCRSS(QN_SYSCON, mask&(~en)); #endif // set PMU to 0.8V, b1 should set PMU after OSC disabled syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_DVDD12_PMU_SET, MASK_ENABLE); }
/** **************************************************************************************** * @brief Enable sleep mode * @param[in] mode sleep mode * @param[in] iconfig wakeup interrupt config * @param[in] callback callback after wakeup * @description * This function is used to set MCU into sleep mode, before enter sleep, wakeup source should be set. ***************************************************************************************** */ void enter_sleep(enum SLEEP_MODE mode, uint32_t iconfig, void (*callback)(void)) { if (mode == SLEEP_CPU_CLK_OFF) { // -------------------------------------------- // cpu clock disable // -------------------------------------------- // Ensure we use deep SLEEP - SLEEPDEEP should be set // SCR[2] = SLEEPDEEP SCB->SCR |= (1UL << 2); // set pd state to deep gating syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_PD_STATE|SYSCON_MASK_PMUENABLE, MASK_DISABLE); GLOBAL_INT_DISABLE(); #if SLEEP_CONFIG_EN == TRUE NVIC->ISER[0] = iconfig; #endif // Wait For Interrupt __WFI(); // Enter sleep mode // Wakeup when interrupt is triggered GLOBAL_INT_RESTORE(); // TODO } else if ((mode == SLEEP_NORMAL) || (mode == SLEEP_DEEP)) { #if QN_LOW_POWER_MODE_EN==TRUE enter_low_power_mode(0); // -------------------------------------------- // cpu clock disable // -------------------------------------------- // Ensure we use deep SLEEP - SLEEPDEEP should be set // SCR[2] = SLEEPDEEP SCB->SCR |= (1UL << 2); #else // -------------------------------------------- // sleep or deep sleep // -------------------------------------------- #ifdef BLE_PRJ // Save configuration before power down save_ble_setting(); #endif #if (defined(QN_9020_B1) && QN_PMU_VOLTAGE) // Switch off REF PLL power syscon_SetPGCR1WithMask(QN_SYSCON, SYSCON_MASK_DIS_REF_PLL, MASK_ENABLE); #endif // switch to internal 20MHz syscon_SetCMDCRWithMask(QN_SYSCON, SYSCON_MASK_CLK_MUX, CLK_INT_20M<<SYSCON_POS_CLK_MUX); if(mode == SLEEP_NORMAL) { #if (QN_DEEP_SLEEP_EN) sleep_env.deep_sleep = false; #endif // power down all module in sleep except 32K and retention memory syscon_SetPGCR0WithMask(QN_SYSCON, sleep_env.retention_modules|0x00000001, 0x00000001); } else { #if (QN_DEEP_SLEEP_EN) sleep_env.deep_sleep = true; #endif // power down all module in deep sleep except retention memory #if (defined(QN_9020_B2) || defined(QN_9020_B1)) syscon_SetPGCR0WithMask(QN_SYSCON, 0xF7FFFCFF, 0xFFFFFC01|~sleep_env.retention_modules); #elif defined(QN_9020_B0) syscon_SetPGCR0WithMask(QN_SYSCON, 0xFFFFFCFF, 0xFFFFFC01|~sleep_env.retention_modules); #endif } #if (defined(QN_9020_B0) && QN_PMU_VOLTAGE) syscon_SetCMDCRWithMask(QN_SYSCON, SYSCON_MASK_AHB_DIV_BYPASS|SYSCON_MASK_AHB_DIVIDER, (0xf<<SYSCON_POS_AHB_DIVIDER)); #endif // Ensure we use deep SLEEP - SLEEPDEEP should be set // SCR[2] = SLEEPDEEP SCB->SCR |= (1UL << 2); // set pd state to sleep #if !QN_PMU_VOLTAGE syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_PD_STATE|SYSCON_MASK_DVDD12_PMU_SET|SYSCON_MASK_PMUENABLE, MASK_ENABLE); #else syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_PD_STATE|SYSCON_MASK_PMUENABLE, MASK_ENABLE); #endif syscon_SetIvrefX32WithMask(QN_SYSCON, SYSCON_MASK_VREG12_A|SYSCON_MASK_VREG12_D|SYSCON_MASK_DVDD12_SW_EN, (0x0 << SYSCON_POS_VREG12_A)|(0x0 << SYSCON_POS_VREG12_D)); #endif // QN_LOW_POWER_MODE_EN==TRUE #if SLEEP_CONFIG_EN == TRUE NVIC->ICPR[0] = 0x00000020; // clear OSC_EN pending flag NVIC->ISER[0] = iconfig; #endif // Wait For Interrupt __WFI(); // Enter sleep mode // Wakeup when sleep timer, comparator or gpio is triggered // Disable interrupt in the wakeup procedure. NVIC->ICER[0] = iconfig; #if QN_LOW_POWER_MODE_EN==TRUE #ifdef BLE_PRJ restore_from_low_power_mode(NULL); #else restore_from_low_power_mode(callback); #endif #else // 1.2V syscon_SetIvrefX32WithMask(QN_SYSCON, SYSCON_MASK_VREG12_A|SYSCON_MASK_VREG12_D|SYSCON_MASK_DVDD12_SW_EN, (0x1 << SYSCON_POS_VREG12_A)|(0x0 << SYSCON_POS_VREG12_D)|SYSCON_MASK_DVDD12_SW_EN); #if (defined(QN_9020_B0) && QN_PMU_VOLTAGE) syscon_SetCMDCRWithMask(QN_SYSCON, SYSCON_MASK_AHB_DIV_BYPASS, MASK_ENABLE); #endif syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_PD_STATE|SYSCON_MASK_DVDD12_PMU_SET, MASK_DISABLE); #if SLEEP_CALLBACK_EN == TRUE if (callback != NULL) { callback(); } #endif #if (defined(QN_9020_B0) && QN_PMU_VOLTAGE) syscon_set_ahb_clk(__AHB_CLK); #endif // 16MHz/32MHz XTAL is ready while (!(syscon_GetBLESR(QN_SYSCON) & SYSCON_MASK_CLK_RDY)) { // XTAL shall be ready before BLE wakeup if(check_ble_wakeup()) { // In this case XTAL wakeup duration is larger than setting. // The parameter 'Oscillator wake-up time' in the NVDS should be revised. #if (QN_DBG_INFO) set_dbg_info(QN_DBG_INFO_XTAL_WAKEUP_DURATION); #endif } } syscon_SetCMDCRWithMask(QN_SYSCON, SYSCON_MASK_CLK_MUX, CLK_XTAL<<SYSCON_POS_CLK_MUX); #endif // QN_LOW_POWER_MODE_EN==TRUE #if ((defined(QN_9020_B2) || defined(QN_9020_B1)) && defined(BLE_PRJ)) sleep_post_process(); #endif } }
int main (void) { SystemInit(); #if 0 if (0x00000004 & inp32(0x40000038)) { outp32(0x40000038, 0x80000000); } else { Led_flash(); while(1); } #endif /* Initialize GPIO */ gpio_init(cb_gpio); #if TEST_SLEEP_NORMAL == TRUE // -------------------------------------------- // sleep wakeup // -------------------------------------------- //set all pin to gpio syscon_SetPMCR0(QN_SYSCON, 0x00000000); syscon_SetPMCR1(QN_SYSCON, 0x00000000); //set all gpio input gpio_set_direction_field(GPIO_PIN_ALL, GPIO_INPUT); gpio_write_pin_field(GPIO_PIN_ALL, (uint32_t)GPIO_HIGH); // pin pull ( 00 : High-Z, 01 : Pull-down, 10 : Pull-up, 11 : Reserved ) syscon_SetPPCR0(QN_SYSCON, 0xAAAA5AAA); // SWD pull-down save 20uA syscon_SetPPCR1(QN_SYSCON, 0x2AAAAAAA); // power down BUCK needed syscon_SetIvrefX32WithMask(QN_SYSCON, SYSCON_MASK_BUCK_BYPASS|SYSCON_MASK_BUCK_DPD, MASK_ENABLE); // power down Flash syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_FLASH_VCC_EN, MASK_DISABLE); // enable dbg power down syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_DBGPMUENABLE, MASK_ENABLE); // dis sar adc buffer syscon_SetPGCR1WithMask(QN_SYSCON, SYSCON_MASK_DIS_SAR_BUF, MASK_ENABLE); Led_flash(); do { delay(10); } while (gpio_read_pin(GPIO_P14) == GPIO_HIGH); sleep_init(); wakeup_by_sleep_timer(__32K_TYPE); wakeup_by_gpio(GPIO_P15, GPIO_WKUP_BY_LOW); do { gpio_set_direction(GPIO_P01, GPIO_INPUT); //enter_sleep(SLEEP_NORMAL, WAKEUP_BY_GPIO, Led_flash); if (wakeup_from_sleeptimer) { sleep_timer_set(32000); wakeup_from_sleeptimer = 0; #if QN_32K_RCO == TRUE clock_32k_correction_enable(clock_32k_correction_cb); #endif } #if QN_32K_RCO == TRUE if (gpio_sleep_allowed() && !dev_get_bf()) #else if (gpio_sleep_allowed()) #endif enter_sleep(SLEEP_NORMAL, WAKEUP_BY_OSC_EN|WAKEUP_BY_GPIO, Led_flash); } while(1); #endif #if TEST_SLEEP_DEEP == TRUE // -------------------------------------------- // deep sleep wakeup // -------------------------------------------- //set all pin to gpio syscon_SetPMCR0(QN_SYSCON, 0x00000000); syscon_SetPMCR1(QN_SYSCON, 0x00000000); //set all gpio input gpio_set_direction_field(GPIO_PIN_ALL, (uint32_t)GPIO_INPUT); gpio_write_pin_field(GPIO_PIN_ALL, (uint32_t)GPIO_HIGH); // pin pull ( 00 : High-Z, 01 : Pull-down, 10 : Pull-up, 11 : Reserved ) syscon_SetPPCR0(QN_SYSCON, 0xAAAA5AAA); // SWD pull-down save 20uA syscon_SetPPCR1(QN_SYSCON, 0x2AAAAAAA); // power down BUCK needed syscon_SetIvrefX32WithMask(QN_SYSCON, SYSCON_MASK_BUCK_BYPASS|SYSCON_MASK_BUCK_DPD, MASK_ENABLE); // power down Flash syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_FLASH_VCC_EN, MASK_DISABLE); // enable dbg power down syscon_SetPGCR2WithMask(QN_SYSCON, SYSCON_MASK_DBGPMUENABLE, MASK_ENABLE); // dis sar adc buffer syscon_SetPGCR1WithMask(QN_SYSCON, SYSCON_MASK_DIS_SAR_BUF, MASK_ENABLE); Led_flash(); do { delay(10); } while (gpio_read_pin(GPIO_P14) == GPIO_HIGH); sleep_init(); do { gpio_set_direction(GPIO_P01, GPIO_INPUT); wakeup_by_gpio(GPIO_P15, GPIO_WKUP_BY_CHANGE); enter_sleep(SLEEP_DEEP, WAKEUP_BY_GPIO, Led_flash); } while(1); #endif #if TEST_SLEEP_CPU_CLK_OFF == TRUE // -------------------------------------------- // clock gating // -------------------------------------------- // Set timer 0 wakeup timer_init(QN_TIMER0, NULL); timer_config(QN_TIMER0, TIMER_PSCAL_DIV, TIMER_COUNT_MS(1000, TIMER_PSCAL_DIV)); timer_enable(QN_TIMER0, MASK_ENABLE); sleep_init(); do { enter_sleep(SLEEP_CPU_CLK_OFF, WAKEUP_BY_TIMER0, NULL); Led_flash(); } while(1); #endif }