# 02 ESP32-Environment Setup-Compilation And Programming

## 一、官方文档

环境搭建的步骤,乐鑫官方都有比较详细的文档,可以直接参考.本文对一些概念进行进一步的学习和探讨.

官方推荐使用Make(ESP-IDF v3.2)或CMake(ESP-IDF v3.3)命令行的方式编译和烧写程序,灵活性更高,参考:

* [详细安装步骤](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/get-started/index.html#get-started-step-by-step)

对于不熟悉命令行操作,喜欢使用集成开发环境的开发者,也提供了IDE的配置指导:

* [Eclipse编译与烧写环境搭建](https://docs.espressif.com/projects/esp-idf/en/latest/get-started-legacy/eclipse-setup.html)

## 二、学习笔记

### 1. 自动切换启动模式的软件方案探究

#### 1.1. 安装串口驱动

在第一节我们提到,V2的开发板使用**CP2102**实现USB转串口的通信(乐鑫原厂开发板大多使用该芯片,其他品牌开发板有些使用CH340),这款芯片在windows10系统中可以自动安装驱动,保持电脑联网,插入设备即可,在Ubuntu16.04、18.04中实测也是可以直接使用的.如果遇到驱动问题,可以在SILABS官网下载.[CP210x USB to UART Bridge VCP Drivers](https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers)

Windows系统可以从设备管理器,查看驱动是否成功安装.Linux系统可以使用dmeg指令查看,可以查找到如下信息,其中**ttyUSB0**就是我们刷固件要用到的端口号.

```
username@dd111:~$ dmesg
[ 3106.111239] usb 1-10: new full-speed USB device number 5 using xhci_hcd
[ 3106.241340] usb 1-10: New USB device found, idVendor=10c4, idProduct=ea60
[ 3106.241350] usb 1-10: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 3106.241357] usb 1-10: Product: CP2102 USB to UART Bridge Controller
[ 3106.241363] usb 1-10: Manufacturer: Silicon Labs
[ 3106.241369] usb 1-10: SerialNumber: 0001
[ 3106.242743] cp210x 1-10:1.0: cp210x converter detected
[ 3106.242985] usb 1-10: cp210x converter now attached to ttyUSB0
```

#### 1.2. 安装esptool.py

```
$ pip install esptool
```

官方esp-idf在进行make flash操作时,也是使用了这个工具

#### 1.3. 查看esptool.py源代码

[esptool.py](https://github.com/espressif/esptool#usage):A serial utility to communicate & flash code to Espressif ESP8266 & ESP32 chips.

**我们先进行一下芯片擦除操作,找一些线索**

```
username@dd111:~$ sudo esptool.py --port /dev/ttyUSB0 erase_flash #可以不设置 --port, 默认为/dev/ttyUSB0
esptool.py v2.7 #esptool版本号
Serial port /dev/ttyUSB0 #操作的端口号 
Connecting...... #尝试连接,与bootloader通信
Detecting chip type... ESP32 #检测到ESP32
Chip is ESP32D0WDQ5 (revision 0) #芯片型号
Features: WiFi, BT, Dual Core, Coding Scheme None #该型号配置
Crystal is 40MHz #晶振频率
MAC: 30:ae:a4:80:05:10 #硬件MAC地址
Uploading stub...
Running stub... 
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 1.1s
Hard resetting via RTS pin...
```

擦除的过程,我们没有对硬件进行任何操作,esptool.py完成了Download boot模式的启动(相当于按住boot,然后点击EN).

根据输出信息顺藤摸瓜,终于在esptool.py文件中发现了实现这个机制的关键代码:

```
    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)
```

```
        # 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
```

模拟BOOT按键被按下

```
            self._setDTR(False) 
            self._setRTS(True)   #DTR=0,RTS=1,IO0=0(相当于BOOT按键被按下)
```

模拟EN按键被按下

```
            self._setDTR(True)   # IO0=LOW
            self._setRTS(False)  # DTR=1,RTS=0,EN=0(硬件复位)
```

这段代码在esp.connect()中调用,也就是esptool.py尝试连接时,就将ESP32设置成了Download boot模式.

### 2. menuconfig作用与原理探究

#### 2.1. ESP-IDF menuconfig作用

make menuconfig 提供了图形化、交互式的配置环境,在Linux编程中用于配置内核.ESP-IDF也提供了这个功能,可用于:

1. 选择通信参数(串口号 波特率 烧写速度等)
2. 配置Bootloader
3. **配置组件参数**
4. 配置编译器(优化等级,屏蔽warning等)
5. 配置自定义参数

#### 2.2. 如何进入menuconfig？

只需要在工程目录下:

```
make menuconfig
```

![menuconfig](https://img-blog.csdnimg.cn/20190723181824535.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIwNTE1NDYx,size_16,color_FFFFFF,t_70)

```
使用键盘操作进入 Component config ---> Wi Fi --->
```

![menuconfig](https://img-blog.csdnimg.cn/20190723182613920.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIwNTE1NDYx,size_16,color_FFFFFF,t_70)

#### 2.3. menuconfig修改了什么？

menuconfig操作完成之后,会在工程根目录下自动生成==sdkconfig==文件,之前的**sdkconfig**被重新命名为**sdkconfig.old**,用于恢复上一次的配置.同目录下还存在一个**sdkconfig.defaults**文件,用于保存初始配置.

**sdkconfig**会被编译系统进一步编译成sdkconfig.h,该文件保存在**工程目录->build->include->sdkconfig.h** 会被构建系统添加到全局头文件搜索路径.

> sdkconfig也可以手动修改,但是要注意,不能在menuconfig图形界面启动时修改,图形界面上的修改会覆盖掉当前手动修改的内容.

#### 2.4. menuconfig添加自定义选项

menuconfig生成的sdkconfig文件中具有一系列的宏定义,可以在工程文件中作为全局宏定义使用,那么我们是否能把自己的宏定义,添加到menuconfig选项中呢,答案是可以的.

menuconfig的自定义菜单项保存在当前工程的==Kconfig.projbuild==文件中,可以按照以下示例编写.我们这里定义了一个一级菜单“TEST Configuration”,二级菜单(宏定义变量名)“TEST\_GPIO”,范围定义为0-34,默认为5,还可以添加help说明文件.

```
menu "TEST Configuration"

config TEST_GPIO
    int "TEST GPIO number"
    range 0 34
    default 5
    help
        test menuconfig

endmenu
```

效果如下:

![menuconfig自定义选项](https://img-blog.csdnimg.cn/20190723193836116.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIwNTE1NDYx,size_16,color_FFFFFF,t_70)

#### 2.4. 为什么使用make调用menuconfig？

虽然make常常被用于编译,但它不等同于编译命令.更广泛的可以理解为对一组命令(makefile)的调用.在ESP-IDF中包含了很多脚本工具,比如esptool.py、idf.py、idf\_monitor.py等,每一个工具都有自己的指令集合,make对其做了进一步的封装,简化了使用.

**例如:**

```
make flash
```

**其实相当于:**

先调用gcc命令将代码编译成bin,再使用esptool烧写程序

```
sudo esptool.py --port /dev/ttyUSB0 erase_flash
```

如果添加自定义make参数,需要将命令参数添加到到makefile中的 [.PHONY](https://www.cnblogs.com/idorax/p/9306528.html)中,防止与文件名冲突(makefile 默认target为文件)

### 3. makefile包含哪些内容？

ESP工具链的make指令功能繁多,但是因为层级关系比较分明,makefile并不复杂,最小的makefile可以只有两行:

```
PROJECT_NAME := myProject
include $(IDF_PATH)/make/project.mk
```

* PROJECT\_NAME是最终bin文件的名称
* \==project.mk==用来管理系统定义的一系列make指令,project.mk中又调用了其他工具的makefile,如调用project\_config.mk来支持make menuconfig,该文件一般不需要修改.

[构建系统 (传统 GNU Make)](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/api-guides/build-system-legacy.html)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://libooogo.gitbook.io/esplane/esp32-development-notes/02esp32-huan-jing-da-jian-bian-yi-yu-shao-xie.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
