ESP32 RTC External Wakeup
来自Jack's Lab
(版本间的差异)
(以“== Overview == 800px <br><br> == Quick Start == In Linux: === Install ESP-IDF === <source lang=bash> $ sudo apt-get install git...”为内容创建页面) |
(→API) |
||
| (未显示1个用户的13个中间版本) | |||
| 第24行: | 第24行: | ||
<br><br> | <br><br> | ||
| − | === Compile | + | === Compile === |
<source lang=bash> | <source lang=bash> | ||
$ cd esp-idf/examples/20_ext_wakeup | $ cd esp-idf/examples/20_ext_wakeup | ||
$ make menuconfig | $ make menuconfig | ||
| + | $ make flash -j2 | ||
| + | </source> | ||
| + | |||
| + | <br><br> | ||
| + | |||
| + | === Upload === | ||
| + | |||
| + | <source lang=bash> | ||
$ make flash | $ make flash | ||
</source> | </source> | ||
| + | You need to press the RST buttom after uploading the firmware into flash. If you guys do not like to do this please patch the /path/to/esp-idf/components/esptool_py/esptool/esptool.py : | ||
| + | |||
| + | <source lang=bash> | ||
| + | diff --git a/esptool.py b/esptool.py | ||
| + | index 755f4cb..ff92c91 100755 | ||
| + | --- a/esptool.py | ||
| + | +++ b/esptool.py | ||
| + | @@ -197,6 +197,12 @@ class ESPLoader(object): | ||
| + | + '\xc0' | ||
| + | self._port.write(buf) | ||
| + | |||
| + | + def reset_to_app(self): | ||
| + | + self._port.setDTR(False) | ||
| + | + self._port.setRTS(True) | ||
| + | + time.sleep(0.05) | ||
| + | + self._port.setRTS(True) | ||
| + | + | ||
| + | """ Calculate checksum of a blob, as it is defined by the ROM """ | ||
| + | @staticmethod | ||
| + | def checksum(data, state=ESP_CHECKSUM_MAGIC): | ||
| + | @@ -1421,7 +1427,6 @@ def dump_mem(esp, args): | ||
| + | sys.stdout.flush() | ||
| + | print 'Done!' | ||
| + | |||
| + | - | ||
| + | def write_flash(esp, args): | ||
| + | """Write data to flash | ||
| + | """ | ||
| + | @@ -1503,6 +1508,7 @@ def write_flash(esp, args): | ||
| + | if args.verify: | ||
| + | print 'Verifying just-written flash...' | ||
| + | verify_flash(esp, args, header_block) | ||
| + | + esp.reset_to_app() | ||
| + | |||
| + | |||
| + | def image_info(args): | ||
| + | </source> | ||
| + | |||
| + | Then Quantum can reset to run your app automatically after uploading the firmware into flash | ||
| + | |||
| + | <br><br> | ||
| + | |||
| + | === Connections === | ||
| + | |||
| + | Connect a Blue/Yellow LED from Quantum_D7_GPIO27 to GND: | ||
| + | |||
| + | * LED_Anode <-----> Quantum_D7_GPIO27 | ||
| + | * LED_Cathode <----> Quantum_GND | ||
| + | |||
| + | |||
| + | The LED would flash two times when the board boot up and then enter deep sleep mode. Connecting the GPIO35 (RTC_IO5) to 3V3 when you guys need to wakeup the ESP32 | ||
| + | |||
| + | <br><br> | ||
| + | |||
| + | == API == | ||
| + | |||
| + | From investigation of [[ESP32 RTC]] | ||
| + | |||
| + | <source lang=c> | ||
| + | #define DEEP_SLEEP_PD_NORMAL BIT(0) /* Base deep sleep mode */ | ||
| + | #define DEEP_SLEEP_PD_RTC_PERIPH BIT(1) /* Power down RTC peripherals */ | ||
| + | #define DEEP_SLEEP_PD_RTC_SLOW_MEM BIT(2) /* Power down RTC SLOW memory */ | ||
| + | #define DEEP_SLEEP_PD_RTC_FAST_MEM BIT(3) /* Power down RTC FAST memory */ | ||
| + | |||
| + | /* | ||
| + | * @brief Prepare for entering sleep mode | ||
| + | * @param deep_slp DEEP_SLEEP_PD_ flags combined with OR (DEEP_SLEEP_PD_NORMAL must be included) | ||
| + | * @param cpu_lp_mode for deep sleep, should be 0 | ||
| + | */ | ||
| + | void rtc_slp_prep_lite(uint32_t deep_slp, uint32_t cpu_lp_mode); | ||
| + | |||
| + | #define RTC_EXT_EVENT0_TRIG BIT(0) | ||
| + | #define RTC_EXT_EVENT1_TRIG BIT(1) | ||
| + | #define RTC_GPIO_TRIG BIT(2) /* Only available in light sleep */ | ||
| + | #define RTC_TIMER_EXPIRE BIT(3) | ||
| + | #define RTC_SDIO_TRIG BIT(4) | ||
| + | #define RTC_MAC_TRIG BIT(5) | ||
| + | #define RTC_UART0_TRIG BIT(6) | ||
| + | #define RTC_UART1_TRIG BIT(7) | ||
| + | #define RTC_TOUCH_TRIG BIT(8) | ||
| + | #define RTC_SAR_TRIG BIT(9) | ||
| + | #define RTC_BT_TRIG BIT(10) | ||
| + | |||
| + | /* | ||
| + | * @brief Enter sleep mode for given number of cycles | ||
| + | * @param cycles_h higher 32 bit part of number of slow clock cycles | ||
| + | * @param cycles_l lower 32 bit part of number of slow clock cycles | ||
| + | * @param wakeup_opt wake up reason to enable (RTC_xxx_TRIG flags combined with OR) | ||
| + | * @param reject_opt reserved, should be 0 | ||
| + | * @return TBD | ||
| + | */ | ||
| + | uint32_t rtc_sleep(uint32_t cycles_h, uint32_t cycles_l, uint32_t wakeup_opt, uint32_t reject_opt); | ||
| + | |||
| + | typedef enum { | ||
| + | RTC_GPIO0_SEL = BIT(0), | ||
| + | RTC_GPIO1_SEL = BIT(1), | ||
| + | RTC_GPIO2_SEL = BIT(2), | ||
| + | RTC_GPIO3_SEL = BIT(3), | ||
| + | RTC_GPIO4_SEL = BIT(4), | ||
| + | RTC_GPIO5_SEL = BIT(5), | ||
| + | RTC_GPIO6_SEL = BIT(6), | ||
| + | RTC_GPIO7_SEL = BIT(7), | ||
| + | RTC_GPIO8_SEL = BIT(8), | ||
| + | RTC_GPIO9_SEL = BIT(9), | ||
| + | RTC_GPIO10_SEL = BIT(10), | ||
| + | RTC_GPIO11_SEL = BIT(11), | ||
| + | RTC_GPIO12_SEL = BIT(12), | ||
| + | RTC_GPIO13_SEL = BIT(13), | ||
| + | RTC_GPIO14_SEL = BIT(14), | ||
| + | RTC_GPIO15_SEL = BIT(15), | ||
| + | RTC_GPIO16_SEL = BIT(16), | ||
| + | RTC_GPIO17_SEL = BIT(17) | ||
| + | } rtc_gpio_sel_t; | ||
| + | |||
| + | void rtc_pads_muxsel(rtc_gpio_sel_t rtc_io_sel, uint8_t set); | ||
| + | void rtc_pads_funsel(rtc_gpio_sel_t rtc_io_sel, uint8_t set); | ||
| + | void rtc_pads_slpsel(rtc_gpio_sel_t rtc_io_sel, uint8_t set); | ||
| + | |||
| + | void rtc_pads_hold(rtc_gpio_sel_t rtc_io_sel, uint8_t set); | ||
| + | void rtc_pads_pu(rtc_gpio_sel_t rtc_io_sel, uint8_t set); | ||
| + | void rtc_pads_pd(rtc_gpio_sel_t rtc_io_sel, uint8_t set); | ||
| + | void rtc_pads_slpie(rtc_gpio_sel_t rtc_io_sel, uint8_t set); | ||
| + | void rtc_pads_slpoe(rtc_gpio_sel_t rtc_io_sel, uint8_t set); | ||
| + | void rtc_pads_funie(rtc_gpio_sel_t rtc_io_sel, uint8_t set); | ||
| + | |||
| + | /* | ||
| + | * Using the EXT_WAKEUP0 slot. So you need to enable the RTC_EXT_EVENT0_TRIG | ||
| + | * in rtc_sleep() | ||
| + | * | ||
| + | * rtc_io_num is the number of rtc_pad. e.g. The number of RTC_GPIO5 is 5 | ||
| + | * wakeup_level = 0: external wakeup at low level | ||
| + | * wakeup_level = 1: external wakeup at high level | ||
| + | */ | ||
| + | void rtc_pad_ext_wakeup(rtc_gpio_sel_t rtc_io_sel, uint8_t rtc_io_num, uint8_t wakeup_level); | ||
| + | </source> | ||
| + | |||
| + | <br><br> | ||
| + | |||
| + | == Code == | ||
| + | |||
| + | === Using EXT_WAKEUP0 === | ||
| + | |||
| + | <source lang=c> | ||
| + | void deep_sleep_ext_wakeup(uint8_t rtc_gpio_num) | ||
| + | { | ||
| + | if (esp_get_deep_sleep_wake_stub() == NULL) { | ||
| + | esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep); | ||
| + | } | ||
| + | |||
| + | rtc_pad_ext_wakeup(1 << rtc_gpio_num, rtc_gpio_num, 1); | ||
| + | |||
| + | /* fixed bug of rtc_pad_slpie() for RTC_GPIO4 */ | ||
| + | //REG_SET_BIT(RTC_IO_ADC_PAD_REG, RTC_IO_ADC1_SLP_IE); | ||
| + | |||
| + | /* fixed bug of rtc_pad_slpie() for RTC_GPIO5 */ | ||
| + | REG_SET_BIT(RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_SLP_IE); | ||
| + | |||
| + | rtc_slp_prep_lite(DEEP_SLEEP_PD_NORMAL, 0); | ||
| + | |||
| + | rtc_sleep(0, 0, RTC_EXT_EVENT0_TRIG, 0); | ||
| + | |||
| + | while (1) { | ||
| + | ; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | #define BLINK_GPIO 27 | ||
| + | |||
| + | void study_task(void *pvParameters) | ||
| + | { | ||
| + | gpio_pad_select_gpio(BLINK_GPIO); | ||
| + | gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); | ||
| + | |||
| + | while (1) { | ||
| + | gpio_set_level(BLINK_GPIO, 0); | ||
| + | vTaskDelay(1000 / portTICK_RATE_MS); | ||
| + | gpio_set_level(BLINK_GPIO, 1); | ||
| + | vTaskDelay(1000 / portTICK_RATE_MS); | ||
| + | gpio_set_level(BLINK_GPIO, 0); | ||
| + | vTaskDelay(1000 / portTICK_RATE_MS); | ||
| + | gpio_set_level(BLINK_GPIO, 1); | ||
| + | vTaskDelay(1000 / portTICK_RATE_MS); | ||
| + | |||
| + | /* pull up the RTC_GPIO5 (GPIO35) to wakeup */ | ||
| + | deep_sleep_ext_wakeup(5); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void app_main() | ||
| + | { | ||
| + | printf("Welcome to Noduino Quantum\r\n"); | ||
| + | printf("Try to investigate the ULP/RTC of ESP32 ... \r\n"); | ||
| + | xTaskCreatePinnedToCore(&study_task, "study_task", 1024, NULL, 5, NULL, 0); | ||
| + | } | ||
| + | </source> | ||
| + | |||
| + | <br> | ||
| + | |||
| + | === Using EXT_WAKEUP1 === | ||
| + | |||
| + | <source lang=c> | ||
| + | /* use the rtc_gpio5 (pull up) to wakeup */ | ||
| + | void ext_wakeup_rtc_io5_setup() | ||
| + | { | ||
| + | rtc_pads_muxsel(1<<5, 1); | ||
| + | rtc_pads_funsel(1<<5, 0); | ||
| + | rtc_pads_slpsel(1<<5, 1); | ||
| + | |||
| + | REG_SET_BIT(RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_SLP_IE); | ||
| + | |||
| + | #if USE_EXT_WAKEUP0 | ||
| + | REG_SET_BIT(RTC_CNTL_EXT_WAKEUP_CONF_REG, RTC_CNTL_EXT_WAKEUP0_LV); | ||
| + | REG_WRITE(RTC_IO_EXT_WAKEUP0_REG, 5<<RTC_IO_EXT_WAKEUP0_SEL_S); | ||
| + | #else | ||
| + | REG_SET_BIT(RTC_CNTL_EXT_WAKEUP_CONF_REG, RTC_CNTL_EXT_WAKEUP1_LV); | ||
| + | REG_SET_BIT(RTC_CNTL_EXT_WAKEUP1_REG, 1<<5); | ||
| + | #endif | ||
| + | } | ||
| + | |||
| + | void deep_sleep_ext_wakeup(uint8_t rtc_gpio_num) | ||
| + | { | ||
| + | if (esp_get_deep_sleep_wake_stub() == NULL) { | ||
| + | esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep); | ||
| + | } | ||
| + | |||
| + | ext_wakeup_rtc_io5_setup(); | ||
| + | |||
| + | rtc_slp_prep_lite(DEEP_SLEEP_PD_NORMAL, 0); | ||
| + | rtc_sleep(0, 0, RTC_EXT_EVENT1_TRIG, 0); | ||
| + | |||
| + | while (1) { | ||
| + | ; | ||
| + | } | ||
| + | } | ||
| + | </source> | ||
| + | |||
| + | <br><br> | ||
| + | |||
| + | == Reference == | ||
| + | |||
| + | ;;For more information please refer to: | ||
| + | |||
| + | * [[Noduino]] | ||
| + | * [[Noduino Quantum]] | ||
| + | |||
| + | <br><br> | ||
<br><br> | <br><br> | ||
<br><br> | <br><br> | ||
2016年12月4日 (日) 01:01的最后版本
目录 |
[编辑] 1 Overview
[编辑] 2 Quick Start
In Linux:
[编辑] 2.1 Install ESP-IDF
$ sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial $ wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux32-1.22.0-59.tar.gz $ mkdir -p toolchain $ tar zxf xtensa-esp32-elf-linux32-1.22.0-59.tar.gz -C toolchain $ export PATH=$PATH:`pwd`/toolchain/xtensa-esp32-elf/bin $ $ git clone --recursive git://github.com/icamgo/esp-idf.git $ export IDF_PATH=`pwd`/esp-idf
[编辑] 2.2 Compile
$ cd esp-idf/examples/20_ext_wakeup $ make menuconfig $ make flash -j2
[编辑] 2.3 Upload
$ make flash
You need to press the RST buttom after uploading the firmware into flash. If you guys do not like to do this please patch the /path/to/esp-idf/components/esptool_py/esptool/esptool.py :
diff --git a/esptool.py b/esptool.py
index 755f4cb..ff92c91 100755
--- a/esptool.py
+++ b/esptool.py
@@ -197,6 +197,12 @@ class ESPLoader(object):
+ '\xc0'
self._port.write(buf)
+ def reset_to_app(self):
+ self._port.setDTR(False)
+ self._port.setRTS(True)
+ time.sleep(0.05)
+ self._port.setRTS(True)
+
""" Calculate checksum of a blob, as it is defined by the ROM """
@staticmethod
def checksum(data, state=ESP_CHECKSUM_MAGIC):
@@ -1421,7 +1427,6 @@ def dump_mem(esp, args):
sys.stdout.flush()
print 'Done!'
-
def write_flash(esp, args):
"""Write data to flash
"""
@@ -1503,6 +1508,7 @@ def write_flash(esp, args):
if args.verify:
print 'Verifying just-written flash...'
verify_flash(esp, args, header_block)
+ esp.reset_to_app()
def image_info(args):
Then Quantum can reset to run your app automatically after uploading the firmware into flash
[编辑] 2.4 Connections
Connect a Blue/Yellow LED from Quantum_D7_GPIO27 to GND:
- LED_Anode <-----> Quantum_D7_GPIO27
- LED_Cathode <----> Quantum_GND
The LED would flash two times when the board boot up and then enter deep sleep mode. Connecting the GPIO35 (RTC_IO5) to 3V3 when you guys need to wakeup the ESP32
[编辑] 3 API
From investigation of ESP32 RTC
#define DEEP_SLEEP_PD_NORMAL BIT(0) /* Base deep sleep mode */
#define DEEP_SLEEP_PD_RTC_PERIPH BIT(1) /* Power down RTC peripherals */
#define DEEP_SLEEP_PD_RTC_SLOW_MEM BIT(2) /* Power down RTC SLOW memory */
#define DEEP_SLEEP_PD_RTC_FAST_MEM BIT(3) /* Power down RTC FAST memory */
/*
* @brief Prepare for entering sleep mode
* @param deep_slp DEEP_SLEEP_PD_ flags combined with OR (DEEP_SLEEP_PD_NORMAL must be included)
* @param cpu_lp_mode for deep sleep, should be 0
*/
void rtc_slp_prep_lite(uint32_t deep_slp, uint32_t cpu_lp_mode);
#define RTC_EXT_EVENT0_TRIG BIT(0)
#define RTC_EXT_EVENT1_TRIG BIT(1)
#define RTC_GPIO_TRIG BIT(2) /* Only available in light sleep */
#define RTC_TIMER_EXPIRE BIT(3)
#define RTC_SDIO_TRIG BIT(4)
#define RTC_MAC_TRIG BIT(5)
#define RTC_UART0_TRIG BIT(6)
#define RTC_UART1_TRIG BIT(7)
#define RTC_TOUCH_TRIG BIT(8)
#define RTC_SAR_TRIG BIT(9)
#define RTC_BT_TRIG BIT(10)
/*
* @brief Enter sleep mode for given number of cycles
* @param cycles_h higher 32 bit part of number of slow clock cycles
* @param cycles_l lower 32 bit part of number of slow clock cycles
* @param wakeup_opt wake up reason to enable (RTC_xxx_TRIG flags combined with OR)
* @param reject_opt reserved, should be 0
* @return TBD
*/
uint32_t rtc_sleep(uint32_t cycles_h, uint32_t cycles_l, uint32_t wakeup_opt, uint32_t reject_opt);
typedef enum {
RTC_GPIO0_SEL = BIT(0),
RTC_GPIO1_SEL = BIT(1),
RTC_GPIO2_SEL = BIT(2),
RTC_GPIO3_SEL = BIT(3),
RTC_GPIO4_SEL = BIT(4),
RTC_GPIO5_SEL = BIT(5),
RTC_GPIO6_SEL = BIT(6),
RTC_GPIO7_SEL = BIT(7),
RTC_GPIO8_SEL = BIT(8),
RTC_GPIO9_SEL = BIT(9),
RTC_GPIO10_SEL = BIT(10),
RTC_GPIO11_SEL = BIT(11),
RTC_GPIO12_SEL = BIT(12),
RTC_GPIO13_SEL = BIT(13),
RTC_GPIO14_SEL = BIT(14),
RTC_GPIO15_SEL = BIT(15),
RTC_GPIO16_SEL = BIT(16),
RTC_GPIO17_SEL = BIT(17)
} rtc_gpio_sel_t;
void rtc_pads_muxsel(rtc_gpio_sel_t rtc_io_sel, uint8_t set);
void rtc_pads_funsel(rtc_gpio_sel_t rtc_io_sel, uint8_t set);
void rtc_pads_slpsel(rtc_gpio_sel_t rtc_io_sel, uint8_t set);
void rtc_pads_hold(rtc_gpio_sel_t rtc_io_sel, uint8_t set);
void rtc_pads_pu(rtc_gpio_sel_t rtc_io_sel, uint8_t set);
void rtc_pads_pd(rtc_gpio_sel_t rtc_io_sel, uint8_t set);
void rtc_pads_slpie(rtc_gpio_sel_t rtc_io_sel, uint8_t set);
void rtc_pads_slpoe(rtc_gpio_sel_t rtc_io_sel, uint8_t set);
void rtc_pads_funie(rtc_gpio_sel_t rtc_io_sel, uint8_t set);
/*
* Using the EXT_WAKEUP0 slot. So you need to enable the RTC_EXT_EVENT0_TRIG
* in rtc_sleep()
*
* rtc_io_num is the number of rtc_pad. e.g. The number of RTC_GPIO5 is 5
* wakeup_level = 0: external wakeup at low level
* wakeup_level = 1: external wakeup at high level
*/
void rtc_pad_ext_wakeup(rtc_gpio_sel_t rtc_io_sel, uint8_t rtc_io_num, uint8_t wakeup_level);
[编辑] 4 Code
[编辑] 4.1 Using EXT_WAKEUP0
void deep_sleep_ext_wakeup(uint8_t rtc_gpio_num)
{
if (esp_get_deep_sleep_wake_stub() == NULL) {
esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep);
}
rtc_pad_ext_wakeup(1 << rtc_gpio_num, rtc_gpio_num, 1);
/* fixed bug of rtc_pad_slpie() for RTC_GPIO4 */
//REG_SET_BIT(RTC_IO_ADC_PAD_REG, RTC_IO_ADC1_SLP_IE);
/* fixed bug of rtc_pad_slpie() for RTC_GPIO5 */
REG_SET_BIT(RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_SLP_IE);
rtc_slp_prep_lite(DEEP_SLEEP_PD_NORMAL, 0);
rtc_sleep(0, 0, RTC_EXT_EVENT0_TRIG, 0);
while (1) {
;
}
}
#define BLINK_GPIO 27
void study_task(void *pvParameters)
{
gpio_pad_select_gpio(BLINK_GPIO);
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
while (1) {
gpio_set_level(BLINK_GPIO, 0);
vTaskDelay(1000 / portTICK_RATE_MS);
gpio_set_level(BLINK_GPIO, 1);
vTaskDelay(1000 / portTICK_RATE_MS);
gpio_set_level(BLINK_GPIO, 0);
vTaskDelay(1000 / portTICK_RATE_MS);
gpio_set_level(BLINK_GPIO, 1);
vTaskDelay(1000 / portTICK_RATE_MS);
/* pull up the RTC_GPIO5 (GPIO35) to wakeup */
deep_sleep_ext_wakeup(5);
}
}
void app_main()
{
printf("Welcome to Noduino Quantum\r\n");
printf("Try to investigate the ULP/RTC of ESP32 ... \r\n");
xTaskCreatePinnedToCore(&study_task, "study_task", 1024, NULL, 5, NULL, 0);
}
[编辑] 4.2 Using EXT_WAKEUP1
/* use the rtc_gpio5 (pull up) to wakeup */
void ext_wakeup_rtc_io5_setup()
{
rtc_pads_muxsel(1<<5, 1);
rtc_pads_funsel(1<<5, 0);
rtc_pads_slpsel(1<<5, 1);
REG_SET_BIT(RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_SLP_IE);
#if USE_EXT_WAKEUP0
REG_SET_BIT(RTC_CNTL_EXT_WAKEUP_CONF_REG, RTC_CNTL_EXT_WAKEUP0_LV);
REG_WRITE(RTC_IO_EXT_WAKEUP0_REG, 5<<RTC_IO_EXT_WAKEUP0_SEL_S);
#else
REG_SET_BIT(RTC_CNTL_EXT_WAKEUP_CONF_REG, RTC_CNTL_EXT_WAKEUP1_LV);
REG_SET_BIT(RTC_CNTL_EXT_WAKEUP1_REG, 1<<5);
#endif
}
void deep_sleep_ext_wakeup(uint8_t rtc_gpio_num)
{
if (esp_get_deep_sleep_wake_stub() == NULL) {
esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep);
}
ext_wakeup_rtc_io5_setup();
rtc_slp_prep_lite(DEEP_SLEEP_PD_NORMAL, 0);
rtc_sleep(0, 0, RTC_EXT_EVENT1_TRIG, 0);
while (1) {
;
}
}
[编辑] 5 Reference
- For more information please refer to