YOGYUI

ESP32 Automatic Bootloader 회로 시뮬레이션 (LTSpice) 본문

Hardware/MCU

ESP32 Automatic Bootloader 회로 시뮬레이션 (LTSpice)

요겨 2022. 11. 2. 12:16
반응형

Espressif

Simulate ESP32 automatic bootloader circuit using LTSpice

 

Espressif 사의 ESP32 SOC(System on Chip)에 펌웨어를 다운로드하려면 칩을 Firmware Download Mode로 만든 후 바이너리 파일을 시리얼 통신 (UART)로 전송해야 한다 = serial bootloader

※ SPI(Serial Peripheral Interface) 방식의 flash 모드로도 다운로드할 수 있다

 

https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/boot-mode-selection.html

시리얼 부트로더 모드로 진입하는 방법은 굉장히 간단하다

ESP32 칩에 전원이 인가되기 전 혹은 리셋 단계에서 ESP32의 GPIO0 핀이 LOW 레벨(0V, GND)로 인가되어 있으면 된다

(GPIO2, GPIO12, GPIO15 각각에 대한 제약사항도 존재하지만, 초급 개발자가 크게 신경쓸 필요는 없다)

 

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html

(거의) 모든 종류의 ESP32 개발 키트(development kit)는 스위치 2개가 PCB에 실장되어 있다 

  • EN 버튼: ESP32 칩 리셋, Low when pressed
  • Boot 버튼: ESP32 GPIO0, Low when pressed

따라서, 시리얼 부트로더로 진입하려면 손가락 2개를 사용해서 "Boot 버튼을 누른 상태에서 EN 버튼을 눌렀다 떼"주면 된다


그런데, Espressif사의 공식 ESP32 개발 툴킷인 esp-idf를 사용해서 코딩 후 dev-kit에 플래싱할 경우, 개발자가 별도로 버튼을 누르는 번거로운 과정없이 자동으로 시리얼 부트로더로 진입하게 된다

원리는 간단하다

FTDT/CP210x/CH340x 등 여러 vendor의 USB-to-UART bridge IC를 통해 PC의 USB 포트와 Devkit이 연결되어 있을 경우, esp-idf의 유틸리티 함수들이 정의되어 있는 파이썬 스크립트 esptool.py에서 Bridge IC의 DTR 핀과 RTS 핀을 동적으로 LOW-HIGH 레벨로 제어하게 된다

  • RTS 핀: ESP의 EN = 리셋
  • DTR 핀: ESP의 GPIO0

https://www.silabs.com/documents/public/data-sheets/CP2102-9.pdf

RTS는 Ready to Send, DTR은 Data Terminal Ready의 약자로, UART/USART 통신에서 사용되는 신호선 중 일부이며, 두 신호 모두 Active Low 신호이다 (Bridge의 Output 신호)

 

이렇게 간단하게 결선하면 idf.py 로 플래싱할 때 자동으로 시리얼 부트로더 모드로 진입할 수 있다

 

esp-idf의 esptools.py 원본 스크립트를 보면 관련 구문을 찾을 수 있다

(components > esptool_py > esptool > esptool.py)

class ESPLoader(object):
    def _setDTR(self, state):
        self._port.setDTR(state)
    
    def _setRTS(self, state):
        self._port.setRTS(state)
        # Work-around for adapters on Windows using the usbser.sys driver:
        # generate a dummy change to DTR so that the set-control-line-state
        # request is sent with the updated RTS state and the same DTR state
        self._port.setDTR(self._port.dtr)
    
    def bootloader_reset(self, usb_jtag_serial=False, extra_delay=False):
        """ Issue a reset-to-bootloader, with USB-JTAG-Serial custom reset sequence option
        """
        # 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 usb_jtag_serial:
            # Custom reset sequence, which is required when the device
            # is connecting via its USB-JTAG-Serial peripheral
            self._setRTS(False)
            self._setDTR(False)  # Idle
            time.sleep(0.1)
            self._setDTR(True)  # Set IO0
            self._setRTS(False)
            time.sleep(0.1)
            self._setRTS(True)  # Reset. Note dtr/rts calls inverted so we go through (1,1) instead of (0,0)
            self._setDTR(False)
            self._setRTS(True)  # Extra RTS set for RTS as Windows only propagates DTR on RTS setting
            time.sleep(0.1)
            self._setDTR(False)
            self._setRTS(False)
        else:
            # This fpga delay is for Espressif internal use
            fpga_delay = True if self.FPGA_SLOW_BOOT and os.environ.get("ESPTOOL_ENV_FPGA", "").strip() == "1" else False
            delay = 7 if fpga_delay else 0.5 if extra_delay else 0.05  # 0.5 needed for ESP32 rev0 and rev1

            self._setDTR(False)  # IO0=HIGH
            self._setRTS(True)   # EN=LOW, chip in reset
            time.sleep(0.1)
            self._setDTR(True)   # IO0=LOW
            self._setRTS(False)  # EN=HIGH, chip out of reset
            time.sleep(delay)
            self._setDTR(False)  # IO0=HIGH, done

코드와 주석을 보면 DTR, RTS는 모두 Active Low 신호이므로 각각 연결된 IO0, EN은 반대의 로직 레벨을 갖게 된다

  • _setDTR(False): DTR Low, IO0 High
  • _setDTR(True): DTR High, IO0 Low
  • _setRTS(False): RTS Low, EN High
  • _setRTS(True): RTS High, EN Low

시리얼 부트로더로 진입하고자 할 때, 코드상에서 EN을 LOW로 만들어 ESP32를 리셋 상태로 만든 뒤, IO0를 LOW로 만든 상태에서 EN을 다시 HIGH로 만드는 시퀀스가 구동되는 것을 알 수 있다


그런데, 개발자 키트의 회로도를 보면 DTR과 RTS가 각각 IO0, EN에 1:1로 단순하게 결선되어 있지 않은 것을 볼 수 있다

https://dl.espressif.com/dl/schematics/esp32_devkitc_v4-sch.pdf

USB-to-UART Bridge IC인 CP2102 (Silicon Labs社)가 주로 사용되며, RTS 핀과 DTR 핀은 NPN 트랜지스터 2개 각각의 Base 혹은 Emitter 단에 연결되며, ESP32의 EN 핀과 IO0 핀은 트랜지스터의 Collector 단에 연결된 다소 난해한 회로가 그려져 있다

 

간단하게 로직레벨로만 회로를 따져보자 (약간의 전자회로 전공지식이 필요하다..)

 

  • DTR과 RTS 신호는 NPN 트랜지스터 (Q1, Q2)의 Base, Emitter 단에 연결되어 있다
  • EN과 IO0는 모두 풀업 저항에 의해 default HIGH 상태
  • DTR과 RTS의 신호 레벨이 같을 경우: Q1과 Q2 모두 Base-Emitter간 전압(bias voltage)이 LOW이기 때문에 트랜지스터에 전류가 흐르지 않는다 (deactivated)
  • DTR이 1, RTS가 0일 경우: Q1의 bias voltage가 forward로 걸리기 때문에 Q1에 전류가 흐르게 되며, ESP의 EN 핀은 RTS 핀과 연결(매우 작은 저항)되기 때문에 EN은 LOW가 된다 (Q2는 deactivated 상태, 따라서 IO0는 HIGH)
  • DTR이 0, RTS가 1일 경우: Q2의 bias voltage가 forward로 걸리기 때문에 Q2에 전류가 흐르게 되며, ESP의 IO0 핀은 DTR 핀과 연결(매우 작은 저항)되기 때문에 IO0은 LOW가 된다 (Q1는 deactivated 상태, 따라서 EN는 HIGH)

Dev-kit 회로도에 나와있는 것과 동일하게 로직 테이블을 정리할 수 있다

# DTR RTS EN IO0
1 H H H H
2 L L H H
3 H L L H
4 L H H L

실제 회로는 어떤 식으로 동작하는지 시뮬레이션해보자 (LTSpice)

※ 실제 동작 타이밍과는 맞지 않을 수 있음

Draft1.asc
0.00MB

(1) DTR과 RTS가 모두 HIGH인 상태에서, 순차적으로 DTR과 RTS가 모두 LOW가 된다
이 때, RTS가 먼저 레벨이 바뀌면서 EN이 잠시 LOW가 되는 시점이 있다 

(2) DTR과 RTS가 모두 LOW인 상태에서, DTR이 HIGH가 되면서 EN이 LOW가 되어 ESP32가 리셋 모드가 된다 

(3) DTR을 LOW로, RST를 HIGH로 순차적으로 변경시켜 EN을 HIGH로 만들어 ESP32가 리셋 모드에서 빠져나오게 되는데, 이 때 IO0가 LOW가 되어 있는 상태라 시리얼 부트로더 모드로 부팅이 된다

(4) 잠시 후 DTR을 HIGH로 만들어 IO0를 HIGH로 만들어준다

 

실제 오실로스코프로 시리얼 부트로더 진입 시점에 DTR과 RTS 신호를 찍어봐도 시뮬레이션 결과와 동일한 것을 알 수 있다

(노란색: RTS, 파란색: DTR)


만약 RTS랑 DTR이 각각 EN과 GPIO0에 직접 연결되어 있어도 결과는 동일할 것 같은데 왜 이렇게 미묘한 트랜지스터 회로를 썼을까 생각해보면...

USB-to-UART Bridge IC의 신호 레벨이 ESP32의 신호레벨과 동일하지 않을 때에 정상적으로 동작하게 하기 위한 회로가 아닐까 뇌피셜을 돌려본다 

(EX: Bridge IC의 신호레벨은 +5V, ESP32의 신호레벨은 +3.3V 등)

 

로직 컨버터 IC나 포토커플러같이 신호 레벨을 맞춰주는 주변회로는 트랜지스터 1쌍에 비하면 부피도 크고 칩 가격도 비싸기 때문에, 저렴하게 구성하기 위한 것이 아니었나...라는 생각을 해본다

아님말고~

 

트랜지스터는 적당한 스펙으로 골라도 개당 가격 50원 수준으로 맞출 수 있으니, ESP32 회로 설계할 때 PC의 USB 시리얼 통신으로 플래싱하는 기능을 넣고 싶다면 별다른 고민없이 devkit과 동일하게 회로를 구성하도록 하자~ (결말이 왜이래;)

끝~!

 

반응형

'Hardware > MCU' 카테고리의 다른 글

FreeRTOS 공식 문서  (0) 2022.01.31
Comments