ESP32自动下载电路分析

前言

最近画了块 ESP32 的板子,焊了两块,一块 RESET 有问题,一块 IO0 有问题,导致下载的时候得手动跳线,很是麻烦,于是想搞一下自动下载电路。

硬件原理图分析

自动下载电路图(RST应为RTS)

串口流控机制

  • DTR: Data Terminal Ready,数据终端准备好,低有效
  • RTS:Request To Send,请求发送,低有效

逻辑关系

  • ESP32系统启动模式
管脚 默认 SPI Flash 启动模式 下载启动模式
GPIO0 上拉 1 0
GPIO2 下拉 无关项 0
EN 高有效 无关项 无关项
  • 硬件电路逻辑
DTR RTS EN IO0
0 0 1 1
0 1 1 0
1 0 0 1
1 1 1 1

总结

当 DTR 和 RTS 同时为 0 或者同时为 1 时,三极管 Q1 和 Q2 均为截止状态,此时 EN 和 IO0 的状态由其他电路决定(内部/外部上拉电阻)。
当 DTR 和 RTS 不同时,EN = RTS, IO0 = DTR。
要进入下载模式,就要保证 EN 由 0 变为 1 时,IO0 得保持低电平。

官方下载源码分析

下面是官方 esptool.py 的下载源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def _connect_attempt(self, mode='default_reset', esp32r0_delay=False):
""" A single connection attempt, with esp32r0 workaround options """
# esp32r0_delay is a workaround for bugs with the most common auto reset
# circuit and Windows, if the EN pin on the dev board does not have
# enough capacitance.
#
# Newer dev boards shouldn't have this problem (higher value capacitor
# on the EN pin), and ESP32 revision 1 can't use this workaround as it
# relies on a silicon bug.
#
# Details: https://github.com/espressif/esptool/issues/136
last_error = None

# If we're doing no_sync, we're likely communicating as a pass through
# with an intermediate device to the ESP32
if mode == "no_reset_no_sync":
return last_error

# issue reset-to-bootloader:
# RTS = either CH_PD/EN or nRESET (both active low = chip in reset
# DTR = GPIO0 (active low = boot to flasher)
#
# DTR & RTS are active low signals,
# ie True = pin @ 0V, False = pin @ VCC.
if mode != 'no_reset':
self._setDTR(False) # IO0=HIGH
self._setRTS(True) # EN=LOW, chip in reset
time.sleep(0.1)
if esp32r0_delay:
# Some chips are more likely to trigger the esp32r0
# watchdog reset silicon bug if they're held with EN=LOW
# for a longer period
time.sleep(1.2)
self._setDTR(True) # IO0=LOW
self._setRTS(False) # EN=HIGH, chip out of reset
if esp32r0_delay:
# Sleep longer after reset.
# This workaround only works on revision 0 ESP32 chips,
# it exploits a silicon bug spurious watchdog reset.
time.sleep(0.4) # allow watchdog reset to occur
time.sleep(0.05)
self._setDTR(False) # IO0=HIGH, done

for _ in range(5):
try:
self.flush_input()
self._port.flushOutput()
self.sync()
return None
except FatalError as e:
if esp32r0_delay:
print('_', end='')
else:
print('.', end='')
sys.stdout.flush()
time.sleep(0.05)
last_error = e
return last_error

代码分析

根据注释来看,默认的可能 DTR 直接连接 IO0,RTS 直接连接 EN。但我们这里要根据自动下载电路来分析。因为 python 是解释性语言,语句之间的时间不能忽略。

  1. DTR = 1, RTS = 0; –> EN = 0, IO0 = 1;
  2. DTR = 0, RTS = 0; –> EN = 1, IO0 = 1;
  3. DTR = 0, RTS = 1; –> EN = 1, IO0 = 0;
  4. DTR = 0, RTS = 1; –> EN = 1, IO0 = 1;
管脚 时序
EN ……………./‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’
IO0 ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’\……………./‘’’’’’’’’’’’’’’’

要进入下载模式时, EN 复位时 IO0 应该为低电平,至少应该出现下面的时序。当 EN 由 0 变为 1 前后,IO0 至少应该保持一段低电平以保证进入下载模式。

管脚 时序
EN ……………./‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’
IO0 …………………………../‘’’’’’’’’’’’’’’’

电路分析

在电路中,EN 一端还连着一个 1uF 的电容。根据数字端口特性,输入逻辑电平高为 0.75VDD。按照电容充电时间公式可以进行计算
$$ 0.75VDD = VDD\cdot (1-e^{-t/RC})$$
将 $VDD = 3.3V$、$R = 12K\Omega$、$C = 1\mu F$ 带入上式计算,可以得到
$$t = 16.6ms$$
即 EN 的上升沿跳变会延迟 16.6 毫秒。在 esptool.py 中,RTS = 1 之后延迟了 50 毫秒,即把 IO0 = 0 的时间延长了 50 毫秒,保证 EN 的电平转换的时间段内 IO0 为低电平,从而进入下载模式。

疑惑

  1. 在 esptool.py 中,根据注释,貌似乐鑫是直接把 DTR、RTS 与 EN、IO0 连着,那么这样搞自动下载电路可以吗?
    最初的电路确实是 DTR、RTS 直接接的 EN、IO0,只是因为有部分串口程序自动打开了硬件 flow control,导致默认 RTS 被拉低,ESP 会一直保持在复位状态;详细信息见 https://www.esp32.com/viewtopic.php?t=5731#p24882
  2. 在抄安信可的 NODEMCU 板子时,发现他的自动下载电路没有那颗电容,不知道是怎么回事。

参考链接

  1. 知乎
  2. CSDN
  3. espressif
  4. github