From aeb698f9beb31e1f13faa0190cac0656d21c1caa Mon Sep 17 00:00:00 2001 From: Baoshi Date: Fri, 8 Sep 2023 22:43:25 +0800 Subject: [PATCH 1/2] Update standby examples and add implementation notes. --- examples/standby_autowake/README.md | 3 +- examples/standby_autowake/standby_autowake.c | 24 +++++++------- examples/standby_btn/README.md | 21 +++++++++--- examples/standby_btn/standby_btn.c | 34 +++++++++----------- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/examples/standby_autowake/README.md b/examples/standby_autowake/README.md index 70e71b4e..65e1f12d 100644 --- a/examples/standby_autowake/README.md +++ b/examples/standby_autowake/README.md @@ -6,8 +6,7 @@ This example serves to show how to put the CH32V003 into its lowest power state Power consumption should be around 10uA. -The MCU only toggles the LED and prints a message, then it goes to sleep. -The LED staying on demonstrates that GPIO keeps its state even when the rest of the mcu is in a coma. +GPIO must be put into input pull-up / pull-down mode to have standby current reduced. Based on the groundwork of Marek M. diff --git a/examples/standby_autowake/standby_autowake.c b/examples/standby_autowake/standby_autowake.c index 4cfaaf34..d08effb7 100644 --- a/examples/standby_autowake/standby_autowake.c +++ b/examples/standby_autowake/standby_autowake.c @@ -13,20 +13,23 @@ void AWU_IRQHandler( void ) { int main() { SystemInit(); - Delay_Ms(100); + + // This delay gives us some time to reprogram the device. + // Otherwise if the device enters standby mode we can't + // program it any more. + Delay_Ms(5000); printf("\r\n\r\nlow power example\r\n\r\n"); - RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; - // GPIO D4 Push-Pull - GPIOD->CFGLR &= ~(0xf<<(4*4)); - GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); - GPIOD->OUTDR |= (1 << 4); + // Set all GPIOs to input pull up + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD; + GPIOA->CFGLR = 0b10001000100010001000100010001000; + GPIOA->OUTDR = 0b11111111; + GPIOC->CFGLR = 0b10001000100010001000100010001000; + GPIOC->OUTDR = 0b11111111; + GPIOD->CFGLR = 0b10001000100010001000100010001000; + GPIOD->OUTDR = 0b11111111; - // give the user time to open the terminal connection - //Delay_Ms(5000); - printf("5000ms wait over\r\n"); - // enable power interface module clock RCC->APB1PCENR |= RCC_APB1Periph_PWR; @@ -62,6 +65,5 @@ int main() // restore clock to full speed SystemInit(); printf("\r\nawake, %u\r\n", counter++); - GPIOD->OUTDR ^= (1 << 4); } } diff --git a/examples/standby_btn/README.md b/examples/standby_btn/README.md index 794e8b4c..0e1a0672 100644 --- a/examples/standby_btn/README.md +++ b/examples/standby_btn/README.md @@ -1,15 +1,28 @@ # the deepest slumber +**WARNING: You MUST hard-reboot the CH32V003 to allow it to go into deep sleep. You cannot go from flashing to deep sleep without a hard power cycle.** + This example serves to show how to put the CH32V003 into its lowest power state (standby) and have it wake with a button press. -Power consumption should be around 10uA. +Power consumption should be around 10uA. + +To enter 10uA standby mode you must perform these steps: + +1. Put all GPIOs in input mode (thus no output state can be preserved). +2. Enable AFIO clock and set AFIO_EXTICR to the wakeup channel. +3. Configure EXTI event. +4. Set PWR_CTLR_PDDS bit in PWR_CTLR (Setting PWREN in RCC_APB1PCENR is not required hum?) +5. Set SLEEPDEEP bit in PFIC_SCTLR +6. Call __WFE() to enter standby mode. -The MCU only toggles the LED and prints a message, then it goes back to sleep. -The LED staying on demonstrates that GPIO keeps its state even when the rest of the mcu is in a coma. +Note: +* All GPIOs must be in input pull-up or pull-down mode before entering standby. Input floating mode result in 100uA current. +* Once CH32V003 enters standby mode, it won't respond to any SWDIO command, therefor cannot be reprogrammed. User must provide a way to have the processor stay awake for reprogramming, e.g. some delay at startup. +* Debug circuitry will consume power. If minichlink terminal is active (including immediately after flashing), standby current will stay around 1.2mA until power cycle. Based on the groundwork of Marek M. ## circuit -Connect LED to PD4 (with resistor), connect button to GND and PD2. +Connect button to GND and PD2. There is no debouncing but it should suffice for waking the chip. diff --git a/examples/standby_btn/standby_btn.c b/examples/standby_btn/standby_btn.c index 33f55a89..658e0574 100644 --- a/examples/standby_btn/standby_btn.c +++ b/examples/standby_btn/standby_btn.c @@ -13,27 +13,25 @@ void EXTI7_0_IRQHandler( void ) { int main() { SystemInit(); - Delay_Ms(100); + + // This delay gives us some time to reprogram the device. + // Otherwise if the device enters standby mode we can't + // program it any more. + Delay_Ms(5000); printf("\n\nlow power example\n\n"); - RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; - // GPIO D4 Push-Pull - GPIOD->CFGLR &= ~(0xf<<(4*4)); - GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); - GPIOD->OUTDR |= (1 << 4); - - // give the user time to open the terminal connection - //Delay_Ms(5000); - //printf("5000ms wait over\r\n"); - - // enable alternate IO function module clock - RCC->APB2PCENR |= RCC_AFIOEN; + // Set all GPIOs to input pull up + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD; + GPIOA->CFGLR = 0b10001000100010001000100010001000; + GPIOA->OUTDR = 0b11111111; + GPIOC->CFGLR = 0b10001000100010001000100010001000; + GPIOC->OUTDR = 0b11111111; + GPIOD->CFGLR = 0b10001000100010001000100010001000; + GPIOD->OUTDR = 0b00000100; - // configure button on PD2 as input, pullup - GPIOD->CFGLR &= ~(0xf<<(2*4)); - GPIOD->CFGLR |= (GPIO_CNF_IN_PUPD)<<(2*4); - GPIOD->BSHR = (1 << 2); + // AFIO is needed for EXTI + RCC->APB2PCENR |= RCC_AFIOEN; // assign pin 2 interrupt from portD (0b11) to EXTI channel 2 AFIO->EXTICR |= (uint32_t)(0b11 << (2 * 2)); @@ -56,6 +54,6 @@ int main() // restore clock to full speed SystemInit(); printf("\nawake, %u\n", counter++); - GPIOD->OUTDR ^= (1 << 4); + Delay_Ms(5000); // wake and reflash can happen here } } From e44c7f40c297c76ed6d457cb85977f01742cab8a Mon Sep 17 00:00:00 2001 From: Baoshi Date: Sat, 23 Sep 2023 14:00:11 +0800 Subject: [PATCH 2/2] Demonstrate proper configuration of GPIO prior to standby. --- examples/standby_autowake/README.md | 2 +- examples/standby_autowake/standby_autowake.c | 47 ++++++++++++----- examples/standby_btn/README.md | 18 ++++--- examples/standby_btn/standby_btn.c | 54 ++++++++++++++------ 4 files changed, 84 insertions(+), 37 deletions(-) diff --git a/examples/standby_autowake/README.md b/examples/standby_autowake/README.md index 65e1f12d..2fe3d123 100644 --- a/examples/standby_autowake/README.md +++ b/examples/standby_autowake/README.md @@ -6,7 +6,7 @@ This example serves to show how to put the CH32V003 into its lowest power state Power consumption should be around 10uA. -GPIO must be put into input pull-up / pull-down mode to have standby current reduced. +Refer to the standby_btn example for GPIO settings. Based on the groundwork of Marek M. diff --git a/examples/standby_autowake/standby_autowake.c b/examples/standby_autowake/standby_autowake.c index d08effb7..9e41489a 100644 --- a/examples/standby_autowake/standby_autowake.c +++ b/examples/standby_autowake/standby_autowake.c @@ -3,12 +3,6 @@ #include "ch32v003fun.h" #include -/* somehow this ISR won't get called?? -void AWU_IRQHandler( void ) __attribute__((interrupt)); -void AWU_IRQHandler( void ) { - GPIOD->OUTDR ^= (1 << 4); -} -*/ int main() { @@ -23,12 +17,41 @@ int main() // Set all GPIOs to input pull up RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD; - GPIOA->CFGLR = 0b10001000100010001000100010001000; - GPIOA->OUTDR = 0b11111111; - GPIOC->CFGLR = 0b10001000100010001000100010001000; - GPIOC->OUTDR = 0b11111111; - GPIOD->CFGLR = 0b10001000100010001000100010001000; - GPIOD->OUTDR = 0b11111111; + // GPIOA: Set to output + GPIOA->CFGLR = (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*1)); + GPIOA->BSHR = GPIO_BSHR_BS2 | GPIO_BSHR_BR1; + GPIOC->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) | + (GPIO_CNF_IN_PUPD<<(4*6)) | + (GPIO_CNF_IN_PUPD<<(4*5)) | + (GPIO_CNF_IN_PUPD<<(4*4)) | + (GPIO_CNF_IN_PUPD<<(4*3)) | + (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*1)) | + (GPIO_CNF_IN_PUPD<<(4*0)); + GPIOC->BSHR = GPIO_BSHR_BS7 | + GPIO_BSHR_BS6 | + GPIO_BSHR_BS5 | + GPIO_BSHR_BS4 | + GPIO_BSHR_BS3 | + GPIO_BSHR_BS2 | + GPIO_BSHR_BS1 | + GPIO_BSHR_BS0; + GPIOD->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) | + (GPIO_CNF_IN_PUPD<<(4*6)) | + (GPIO_CNF_IN_PUPD<<(4*5)) | + (GPIO_CNF_IN_PUPD<<(4*4)) | + (GPIO_CNF_IN_PUPD<<(4*3)) | + (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*0)); + GPIOD->BSHR = GPIO_BSHR_BS7 | + GPIO_BSHR_BS6 | + GPIO_BSHR_BS5 | + GPIO_BSHR_BS4 | + GPIO_BSHR_BS3 | + GPIO_BSHR_BS2 | + GPIO_BSHR_BS0; + // enable power interface module clock RCC->APB1PCENR |= RCC_APB1Periph_PWR; diff --git a/examples/standby_btn/README.md b/examples/standby_btn/README.md index 0e1a0672..4b6421c9 100644 --- a/examples/standby_btn/README.md +++ b/examples/standby_btn/README.md @@ -4,19 +4,21 @@ This example serves to show how to put the CH32V003 into its lowest power state (standby) and have it wake with a button press. -Power consumption should be around 10uA. +Power consumption should be around 9uA. To enter 10uA standby mode you must perform these steps: -1. Put all GPIOs in input mode (thus no output state can be preserved). -2. Enable AFIO clock and set AFIO_EXTICR to the wakeup channel. -3. Configure EXTI event. -4. Set PWR_CTLR_PDDS bit in PWR_CTLR (Setting PWREN in RCC_APB1PCENR is not required hum?) -5. Set SLEEPDEEP bit in PFIC_SCTLR -6. Call __WFE() to enter standby mode. +1. GPIOs other than the wake up pin can be set to either input or output mode (see notes). +2. Set GPIO(s) for wake up to input mode with appropriate pull up/down. +3. Enable AFIO clock and set AFIO_EXTICR to the wakeup channel. +4. Configure EXTI event. +5. Set PWR_CTLR_PDDS bit in PWR_CTLR (Setting PWREN in RCC_APB1PCENR is not required hum?) +6. Set SLEEPDEEP bit in PFIC_SCTLR +7. Call __WFE() to enter standby mode. Note: -* All GPIOs must be in input pull-up or pull-down mode before entering standby. Input floating mode result in 100uA current. +* GPIOs in output mode will retain state during standby. +* GPIO if set to input mode must have internal or external pulling resistor. Floating input pin will cause 100uA standby current. * Once CH32V003 enters standby mode, it won't respond to any SWDIO command, therefor cannot be reprogrammed. User must provide a way to have the processor stay awake for reprogramming, e.g. some delay at startup. * Debug circuitry will consume power. If minichlink terminal is active (including immediately after flashing), standby current will stay around 1.2mA until power cycle. diff --git a/examples/standby_btn/standby_btn.c b/examples/standby_btn/standby_btn.c index 658e0574..be6c99b7 100644 --- a/examples/standby_btn/standby_btn.c +++ b/examples/standby_btn/standby_btn.c @@ -3,38 +3,60 @@ #include "ch32v003fun.h" #include -void EXTI7_0_IRQHandler( void ) __attribute__((interrupt)); -void EXTI7_0_IRQHandler( void ) { - //GPIOD->OUTDR ^= (1 << 4); -} - - int main() { SystemInit(); - + // This delay gives us some time to reprogram the device. // Otherwise if the device enters standby mode we can't // program it any more. Delay_Ms(5000); printf("\n\nlow power example\n\n"); - - // Set all GPIOs to input pull up RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD; - GPIOA->CFGLR = 0b10001000100010001000100010001000; - GPIOA->OUTDR = 0b11111111; - GPIOC->CFGLR = 0b10001000100010001000100010001000; - GPIOC->OUTDR = 0b11111111; - GPIOD->CFGLR = 0b10001000100010001000100010001000; - GPIOD->OUTDR = 0b00000100; + // GPIOA: Set to output + GPIOA->CFGLR = ((GPIO_CNF_OUT_PP | GPIO_Speed_2MHz)<<(4*2)) | + ((GPIO_CNF_OUT_PP | GPIO_Speed_2MHz)<<(4*1)); + GPIOA->BSHR = GPIO_BSHR_BS2 | GPIO_BSHR_BR1; + // GPIOC: Set to input with mixed pull-up / pull-down + GPIOC->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) | + (GPIO_CNF_IN_PUPD<<(4*6)) | + (GPIO_CNF_IN_PUPD<<(4*5)) | + (GPIO_CNF_IN_PUPD<<(4*4)) | + (GPIO_CNF_IN_PUPD<<(4*3)) | + (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*1)) | + (GPIO_CNF_IN_PUPD<<(4*0)); + GPIOC->BSHR = GPIO_BSHR_BS7 | + GPIO_BSHR_BR6 | + GPIO_BSHR_BS5 | + GPIO_BSHR_BR4 | + GPIO_BSHR_BS3 | + GPIO_BSHR_BR2 | + GPIO_BSHR_BS1 | + GPIO_BSHR_BR0; + // GPIOD: D2 set to input pull-up + GPIOD->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) | + (GPIO_CNF_IN_PUPD<<(4*6)) | + (GPIO_CNF_IN_PUPD<<(4*5)) | + (GPIO_CNF_IN_PUPD<<(4*4)) | + (GPIO_CNF_IN_PUPD<<(4*3)) | + (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*0)); + GPIOD->BSHR = GPIO_BSHR_BR7 | + GPIO_BSHR_BS6 | + GPIO_BSHR_BR5 | + GPIO_BSHR_BS4 | + GPIO_BSHR_BR3 | + GPIO_BSHR_BS2 | + GPIO_BSHR_BR0; // AFIO is needed for EXTI RCC->APB2PCENR |= RCC_AFIOEN; // assign pin 2 interrupt from portD (0b11) to EXTI channel 2 - AFIO->EXTICR |= (uint32_t)(0b11 << (2 * 2)); + AFIO->EXTICR |= (uint32_t)(0b11 << (2*2)); // enable line2 interrupt event EXTI->EVENR |= EXTI_Line2;