Skip to content

Latest commit

 

History

History
107 lines (88 loc) · 6.11 KB

File metadata and controls

107 lines (88 loc) · 6.11 KB

ULP 协处理器在低功耗模式下操作 RTC GPIO 的例子

本文目的是介绍如何在 ULP 协处理器中操作 RTC GPIO

1. RTC GPIO

RTC GPIO 一共有 18 个 IO 口,这些 GPIO 管脚具有低功耗 RTC 性能和模拟功能,并且由 ESP32 的 RTC 子系统控制。这些功能不使用 IO_MUX 和 GPIO 交换矩阵,而是使用 RTC_MUX 将 IO 指向 RTC 子系统。

当这些管脚被配置为 RTC GPIO 管脚时,作为输出的管脚能够在芯片处于 Deep-Sleep 睡眠状态下保持输出电平值,或者作为输入管脚使用时可以将芯片从 Deep-Sleep 中唤醒。

下表列出了 RTC 管脚和其对应的 GPIO 管脚

RTC GPIO Num GPIO Num Pad Name Analog Function1 Analog Function2 Analog Function3
0 36 SENSOR_VP ADC_H ADC1_CH0 -
1 37 SENSOR_CAPP ADC_H ADC1_CH1 -
2 38 SENSOR_CAPN ADC_H ADC1_CH2 -
3 39 SENSOR_VN - ADC1_CH3 -
4 34 VDET_1 - ADC1_CH6 -
5 35 VDET_2 - ADC1_CH7 -
6 25 GPIO25 DAC_1 ADC2_CH8 -
7 26 GPIO26 DAC_2 ADC2_CH9 -
8 33 32K_XN XTAL_32K_N ADC1_CH5 TOUCH8
9 32 32K_XP XTAL_32K_P ADC1_CH4 TOUCH9
10 4 GPIO4 - ADC2_CH0 TOUCH0
11 0 GPIO0 - ADC2_CH1 TOUCH1
12 2 GPIO2 - ADC2_CH2 TOUCH2
13 15 MTDO - ADC2_CH3 TOUCH3
14 13 MTCK - ADC2_CH4 TOUCH4
15 12 MTDI - ADC2_CH5 TOUCH5
16 14 MTMS - ADC2_CH6 TOUCH6
17 27 GPIO27 - ADC2_CH7 TOUCH7

在本例子中,我们使用 GPIO25,GPIO26 指示 CPU 的工作状态;GPIO4,GPIO27 指示 ULP 协处理器的状态。

PIN 说明
GPIO25 主 CPU 运行时置高,主 CPU 休眠时置低
GPIO26 每当主 CPU 被唤醒时,翻转一次电平状态
GPIO4 ULP 协处理器运行时置高,ULP 协处理器时休眠时置低
GPIO27 每当 ULP 协处理器被唤醒时,翻转一次电平状态

2. RTC GPIO 翻转图示

本例子主 CPU 设置 ULP 协处理器每隔 10ms 唤醒自身一次 并且 ULP 协处理器每隔 8 个唤醒周期后,再唤醒主 CPU 一次,依此循环。每次处理器状态切换时,均有相应的 GPIO 翻转指示当前的状态。

3.软件部分

ESP32 的 C 语言编译环境安装和配置参照 链接地址,另外 ULP 协处理器目前只支持汇编编程,所以还需要安装汇编工具链,下面介绍汇编工具链的安装和配置。

3.1 汇编环境的配置

ULP 协处理器配置汇编编译工具链,只需两步即可安装配置完毕,下面给出 ubuntu 操作系统下配置的步骤,或者点击 链接地址 获得更多 ULP 编程信息

  • 第一步, 下载工具链 binutils-esp32ulp toolchain 链接地址, 解压到需要安装的目录
  • 第二步,添加工具链的 bin 目录到系统环境变量 PATH 中。例如我的解压目录是 /opt/esp32ulp-elf-binutils 那么添加 export PATH=/opt/esp32ulp-elf-binutils/bin:$PATH 这一行到 /home 目录的隐藏文件 .bashrc 文件最后一行,保存关闭文件并使用命令 source .bashrc 使上述环境变量生效

至此,汇编编译环境就安装好了,将 ulp_rtc_gpio/ 目录下的 sdkconfig.defaults 复制一份改名为 sdkconfig,然后在工程中使用 make all -j8 && make flash monitor 编译并烧录命令时,会自动调用汇编工具链编译汇编代码。

3.2 汇编代码介绍

需要注意:每个 GPIO(包括 RTC GPIO)都有单独的 hold 功能,由 RTC 寄存器控制。 GPIO 的 hold 功能被置上后, GPIO 在置上 hold 那一刻的状态被强制保持,无论内部信号如何变化,修改 IO_MUX 配置或者 GPIO 配置,都不会改变 GPIO 的状态。所以如果希望在看门狗超时触发内核复位或系统复位时亦或者 Deep-sleep 时 GPIO 的状态不被改变,就需要提前把相应GPIO 的 hold 功能置位。

在下面的汇编代码中,我们需要操作 RTC_GPIO10, 则先应 disable RTC_GPIO10 管脚的 hold 功能后,再操作 RTC_GPIO10 输出高

	.global entry
entry:
	/* Disable hold of RTC_GPIO10 output */
	WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,0)
	/* Set the RTC_GPIO10 output HIGH to signal that ULP is now up */
	WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG,RTC_GPIO_OUT_DATA_W1TS_S+10,1,1)
	/* Wait some cycles to have visible trace on the scope */
	wait 1000

同理,在进入 Deep-Sleep 之前,拉低 RTC_GPIO10 之后,则需要把 RTC_GPIO10 管脚的 hold 功能置位,使其在系统复位时能够保持原来的电平状态

	.global toggle_complete
toggle_complete:
	/* Set the RTC_GPIO10 output LOW (clear output) to signal that ULP is now going down */
	WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG,RTC_GPIO_OUT_DATA_W1TC_S+10,1,1)
	/* Enable hold on RTC_GPIO10 output */
	WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG,RTC_IO_TOUCH_PAD0_HOLD_S,1,1)
	/* Compare the toggle counter with toggle cycles to wakeup SoC  and wakup SoC if the values match */ 
	and r0, r3, toggle_cycles_to_wakeup
	jump wake_up, eq

4. 额外说明

如果你运行了这个例子,而且试图分析各个处理器切换状态,可能会遇到上图中额外多出的 98ms 的疑惑。这里需要说明的是,汇编程序 Polling CPU 并唤醒 CPU 时间是很短的,绝大部分的时间是用来 boot 主 CPU 的(例如主 CPU 上电动作、一级 Bootloader 加载打印等时间消耗)。因为在 DeepSleep 状态下 CPU 的各个模块都是被关掉的,如内部 8MHz 振荡器、40MHz 高速晶振、PLL 及射频模块均禁用;数字内核断电,CPU 内容丢失,这里有一个重新上电加载的过程。

	/* Get ULP back to sleep */
	.global exit
exit:
	halt
	.global wake_up
wake_up:
	/* Check if the SoC can be woken up */
	READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1)
	and r0, r0, 1
	jump exit, eq
	/* Wake up the SoC and stop ULP program */
	wake
	/* Stop the wakeup timer so it does not restart ULP */
	WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
	halt