ESP32 I2C 设备扫描代码

有时候在调试 I2C 设备的时候,需要确定设备的地址,可以使用下面的代码来完成:

// ESP32 I2C Scanner
// Based on code of Nick Gammon  http://www.gammon.com.au/forum/?id=10896
// ESP32 DevKit - Arduino IDE 1.8.5
// Device tested PCF8574 - Use pullup resistors 3K3 ohms !
// PCF8574 Default Freq 100 KHz 

#include <Wire.h>

void setup()
{
  Serial.begin (115200);  
  Wire.begin (21, 22);   // sda= GPIO_21 /scl= GPIO_22
}

void Scanner ()
{
  Serial.println ();
  Serial.println ("I2C scanner. Scanning ...");
  byte count = 0;

  Wire.begin();
  for (byte i = 8; i < 120; i++)
  {
    Wire.beginTransmission (i);          // Begin I2C transmission Address (i)
    if (Wire.endTransmission () == 0)  // Receive 0 = success (ACK response) 
    {
      Serial.print ("Found address: ");
      Serial.print (i, DEC);
      Serial.print (" (0x");
      Serial.print (i, HEX);     // PCF8574 7 bit address
      Serial.println (")");
      count++;
    }
  }
  Serial.print ("Found ");      
  Serial.print (count, DEC);        // numbers of devices
  Serial.println (" device(s).");
}

void loop()
{
  Scanner ();
  delay (100);
}

代码来自 https://www.esp32.com/viewtopic.php?p=55303

ESP32 和 C# 的 CRC16

CRC-16 有很多种,这里使用的是CRC-16/CCITT-FALSE。可以用这个在线工具进行验证(注意下图是16进制数值):

https://crccalc.com/

1.

static unsigned short crc16(const unsigned char *buf, unsigned long count)
{
  unsigned short crc = 0xFFFF;
  int i;

  while(count--) {
    crc = crc ^ *buf++ << 8;

    for (i=0; i<8; i++) {
      if (crc & 0x8000) crc = crc << 1 ^ 0x1021;
      else crc = crc << 1;
    }
  }
  return crc;
}

void setup() {
        Serial.begin(115200);
}

void loop() {
        byte data[11]={0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,0x11};
        Serial.println(crc16(data,11),HEX);
        delay(10000);
}

2.C# 代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace CRC16Test
{
    class Program
    {


        static void Main(string[] args)
        {
            byte[] data = new byte[] { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,0x11};
            /*           for (int i = 0; i < 10; i++)
                       {
                           data[i] = (byte)(i % 256);
                       }
           */
            string hex = Crc16Ccitt(data).ToString("x2");
            Console.WriteLine(hex);

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }

        //
        // CRC-16/CCITT-FALSE
        // https://crccalc.com/
        //
        public static ushort Crc16Ccitt(byte[] bytes)
        {
            const ushort poly = 0x1021;
            ushort[] table = new ushort[256];
            ushort initialValue = 0xffff;
            ushort temp, a;
            ushort crc = initialValue;
            for (int i = 0; i < table.Length; ++i)
            {
                temp = 0;
                a = (ushort)(i << 8);
                for (int j = 0; j < 8; ++j)
                {
                    if (((temp ^ a) & 0x8000) != 0)
                        temp = (ushort)((temp << 1) ^ poly);
                    else
                        temp <<= 1;
                    a <<= 1;
                }
                table[i] = temp;
            }
            for (int i = 0; i < bytes.Length; ++i)
            {
                crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
            }
            return crc;
        }

    }
}

中文版和英文版的超级终端

从 Windows XP 的中文版和英文版中拷贝而来的超级终端程序。最主要是它提供了 Xmodem,YModem和ZModem 文件传输协议,可以用来进行测试。

Windows XP 的超级终端

=====================================================

2023年1月13日更新

上述的超级终端在 Win10 下可以正常使用,但是运行之后不会出现图标。经过研究这个问题可以通过添加hticons.dll来解决。有需要的朋友可以在下面下载这个文件,放到超级终端同一个目录下即可。

FireBeetle “软”USB Host Shield

喜欢军事的朋友一定经常听过这句话,这就是“只要推力大,板砖也能飞上天”。实际上,这句话并非玩笑之语,确有出处。当年,F-22是从“先进战术战斗机”(简称ATF)计划发展而来的。早期,好几家公司一起竞标,分别是波音,通用动力,洛克希德,诺思罗普,和麦道公司。大家提概念设计方案。结果,可能是因为洛克希德曾经设计出F-117隐形飞机的缘故,走老路当然是最轻松的,所以,洛克希德最初提出的F-22前身方案,它就像是F-117的放大版。设计跟F-117这个怪胎一样,为了隐身性能,以牺牲空气动力学为代价,飞机设计总重36吨。后来,有个人是这么评价这种早期设计的,他就是洛克希德在ATF计划中的项目经理巴特·奥斯本(bart osborne),1998年,一家杂志对奥斯本进行采访【参考1】,而他是这样说的:“我们知道,这个设计将会存在严重的超音速问题,我们的设计当然可以超音速飞行,但肯定蠢得像猪。显然,只要推力大,板砖也能飞上天(With enough power, you can make a brick fly)”

这句话出处就是这么来的,他当时想表达的本意是这样的:我们设计的这种飞机, 在高空高速上肯定会存在很多问题,当然,这并不是说,这种设计不能超音速飞行,完全不是,因为只要推力,你也能让砖头飞上天,也能它超音速。

巴特·奥斯本的评价很中肯,洛克希德提交这个概念设计方案后,美国空军对这种设计的评价最差,在其他7个项目设计中,排倒数第一。【参考1】

对于我们来说,只要 CPU 足够快,GPIO 足够快,能够模拟出各种总线,最常见的是软串口,再稍微复杂一些的是I2C, SPI。最近有一个项目使用 ESP32 来模拟 USB Host,当前因为 GPIO 不够快,目前只能解析键盘鼠标这样的低速设备(Low Speed)。项目地址是:https://github.com/sdima1357/esp32_usb_soft_host 当然,对于Arduino 玩家来说可以直接使用下面这个库 :https://github.com/tobozo/ESP32-USB-Soft-Host

库可以同时支持4个 USB Host, 这里我设计一个 FireBeetle Shield 提供了 2个 USB Host。具体电路如下:

PCB 如下:

Soft USB Host 和 FireBeetle

可以看到板子上有2个 USB 母头和1个USB 公头,其中母头是给 USB Host使用的,公头是为了在供电不足情况下通过充电宝之类直接对设备供电。库可以支持4组USB Host ,经过实验在FireBeetle上可以使用下面的组合:

16131925
17151826

测试代码:

#include <ESP32-USBSoftHost.hpp>
#include "usbkbd.h" // KeyboardReportParser

  // default pins tested on FireBeetle
  // +  16 13 19 25
  // -  17 15 18 26
  #define DP_P0  25  // always enabled
  #define DM_P0  26  // always enabled
  #define DP_P1  19 // -1 to disable
  #define DM_P1  18 // -1 to disable
  #define DP_P2  -1 // -1 to disable
  #define DM_P2  -1 // -1 to disable
  #define DP_P3  -1 // -1 to disable
  #define DM_P3  -1 // -1 to disable



static void my_USB_DetectCB( uint8_t usbNum, void * dev )
{
  sDevDesc *device = (sDevDesc*)dev;
  printf("New device detected on USB#%d\n", usbNum);
  printf("desc.bcdUSB             = 0x%04x\n", device->bcdUSB);
  printf("desc.bDeviceClass       = 0x%02x\n", device->bDeviceClass);
  printf("desc.bDeviceSubClass    = 0x%02x\n", device->bDeviceSubClass);
  printf("desc.bDeviceProtocol    = 0x%02x\n", device->bDeviceProtocol);
  printf("desc.bMaxPacketSize0    = 0x%02x\n", device->bMaxPacketSize0);
  printf("desc.idVendor           = 0x%04x\n", device->idVendor);
  printf("desc.idProduct          = 0x%04x\n", device->idProduct);
  printf("desc.bcdDevice          = 0x%04x\n", device->bcdDevice);
  printf("desc.iManufacturer      = 0x%02x\n", device->iManufacturer);
  printf("desc.iProduct           = 0x%02x\n", device->iProduct);
  printf("desc.iSerialNumber      = 0x%02x\n", device->iSerialNumber);
  printf("desc.bNumConfigurations = 0x%02x\n", device->bNumConfigurations);
  // if( device->iProduct == mySupportedIdProduct && device->iManufacturer == mySupportedManufacturer ) {
  //   myListenUSBPort = usbNum;
  // }
}


static void my_USB_PrintCB(uint8_t usbNum, uint8_t byte_depth, uint8_t* data, uint8_t data_len)
{
  // if( myListenUSBPort != usbNum ) return;
  printf("in: ");
  for(int k=0;k<data_len;k++) {
    printf("0x%02x ", data[k] );
  }
  printf("\n");
}

usb_pins_config_t USB_Pins_Config =
{
  DP_P0, DM_P0,
  DP_P1, DM_P1,
  DP_P2, DM_P2,
  DP_P3, DM_P3
};


void setup()
{

  Serial.begin(115200);
  delay(200);
  Serial.println("USB Test");
  delay(1000);

  USH.init( USB_Pins_Config, my_USB_DetectCB, my_USB_PrintCB );

}

void loop()
{
  vTaskDelete(NULL);
}


运行结果, 上面是输出的Descriptor,下面是收到的鼠标数据:

目前这个库还存在一些兼容性问题,并非所有的鼠标都能正常工作。我测试了微软的IO1.01 是可以正常工作的。

参考:

  1. http://www.codeonemagazine.com/f22_article.html?item_id=179 Design Evolution Of The F-22 Raptor
  2. http://www.nanshannews.cn/cj/cj9059.html

CH55x 串口速度测试

使用上次的工具来测试 Ch55x(Ch551/2/4)开发板的串口速度。

实际测试的是 CH554 ,使用 Ch55xduino 编写代码如下:

#ifndef USER_USB_RAM
#error "This example needs to be compiled with a USER USB setting"
#endif

#include "src/userUsbCdc/USBCDC.h"

//This is a fairly large array, store it in external memory with keyword __xdata
__xdata char recvStr[64];

// 代码中出现的发送和接收
// 测试 Package 的大小
#define MAX_N 256

// 2秒超时
#define TIMEOUT  2000

// 接收数据 Buffer
// 设置为实际的2倍大小避免接收超过Buffer 的问题
__xdata byte receiveBuffer[MAX_N];

void setup() {
  USBInit();
}

// 状态机记录
byte currentStatus = 0;

// 收到的数量
int counter = 0;
// 超时计时器
unsigned long Elsp;
// 发送标记,True 时发送,反之不发送
boolean sendOnce = true;
// 发生错误的数量
int failCount;

void loop() {
  switch (currentStatus) {
    case 0:
      // 初始状态
      while (USBSerial_available()) {
        byte c = USBSerial_read();
        // q命令查询当前状态(用于调试)
        if (c == 'q') {
          USBSerial_print_s("Current 0");
          USBSerial_flush();
        }
        // 1(接收模式) 2(发送模式)
        if ((c == '1') || (c == '2')) {
          // 转移到下一个状态
          currentStatus = c - '0';
          // 如果是接收模式,那么开始计时
          if (c == '1') {
            Elsp = millis();
            counter = 0;
          }
        }
      }
      break;

    case 1: //接收模式
      // 如果接收模式超时,那么直接返回到状态0
      if (millis() - Elsp > TIMEOUT) {
        // 返回状态 0
        currentStatus = 0;
        // 发送 "aaa" 表示当前超时
        USBSerial_write('a'); USBSerial_write('a'); USBSerial_write('a');
        USBSerial_flush();
      }

      // 接收数据
      while (USBSerial_available()) {
        byte c = USBSerial_read();
        receiveBuffer[counter] = c;
        // 统计接收到的数据量
        counter++;

        // 如果接收到足够的数据,那么转移到状态3 进行校验
        if (counter == MAX_N) {
          currentStatus = 3;
          //Serial.print("St3");
        }
      }
      break;
    case 3:
      // 校验接收模式收到的数据,数据是 0x55 0xaa 0x55....
      failCount = 0;
      for (int i = 0; i < MAX_N; i = i + 2) {
        if (receiveBuffer[i] != 0x55) {
          failCount++;
        }
        if (receiveBuffer[i + 1] != 0xAA) {
          failCount++;
        }
      }
      // 检查是否有错误
      if (failCount == 0) {
        // 返回"ppp" 表示校验通过
        USBSerial_write('p'); USBSerial_write('p'); USBSerial_write('p');
        USBSerial_flush();
      }
      else {
        // 校验失败,返回 'f'+发生错误的数量错误数据
        USBSerial_write('f');
        USBSerial_write((byte)(failCount & 0xFF));
        USBSerial_write((byte)(failCount >> 8));

      }
      counter = 0;
      //Serial.println("Switch to status 1");
      Elsp = millis();
      currentStatus = 1;
      break;
    case 2:
      // 发送模式,直接发送 MAX_N 个 0x55 0xaa 0x55 ....
      if (sendOnce) {
        for (int i = 0; i < MAX_N / 2; i++) {
          USBSerial_write(0x55);
          USBSerial_write(0xAA);
          USBSerial_flush();
        }
        sendOnce = false;
      }
      while (USBSerial_available()) {
        char c = USBSerial_read();
        if (c == '0') {
          currentStatus = 0;
          //Serial.print("Switch to 0");
        }
        if (c == '2') {
          sendOnce = true;
        }
        // 查询状态
        if (c == 'q') {
          USBSerial_print_s("Current 2");
          USBSerial_flush();
        }
      }
      break;
    default: {
        USBSerial_println_s("Unknown status");
        USBSerial_flush();
      }
  }

1.PC端接收,Ch554发送,可以看到速度能达到37K/S左右

2.PC端发送,Ch554接收,可以看到速度能达到97K/S左右

从结果可以看出来,比 32U4 这样的芯片还是快一点点的(测试模式是 5V 24Mhz)

Win10 下通过BCDEDIT 增加启动选项

最近在看《软件调试(第二版)》,第四章讲述启动过程的时候作者提到了 bcdedit 相关内容。这次根据指引进行实验。

1.创建一个新的引导项目,这里我们只是将目前的引导选项多制作一个拷贝

通过拷贝生成一个引导选项

2.重启后会出现下面的界面,可以选择

选择引导项

3.《软件调试》书中介绍了下面的命令可以将这个界面切换为 Win7 类似的

切换为 Windows7 风格的启动菜单

4.之后重启启动界面变成下面这种

5.可以在系统下运行 msconfig 编辑启动项

MSConfig

6.使用下面的命令可以恢复为 2. 这种样式

恢复为 Windows 10 的方法

参考

  1. https://www.howtogeek.com/245175/how-to-add-safe-mode-to-the-windows-8-and-10-boot-menu/ How to Add Safe Mode to the Windows 8 and 10 Boot Menu
  2. https://blog.csdn.net/qq_25368751/article/details/80660286 多系统的福音,bcdedit找回传统启动菜单。取消(恢复)Modern UI风格启动菜单~菜单~

SerialRead() 结果为何不是想要的?

代码如下,功能很简单:判断输入的是否为 0xaa, 如果是就输出一段字符,如果不是输出另外一段:

void setup() {
  Serial.begin(9600);
}

void loop() {
  while (Serial.available()) {
    char c = Serial.read();
    if (c == 0xaa) {
      Serial.println("Get 0xaa");
    } else {
      Serial.print("Not 0xaa. You have input ");
      Serial.println(c,HEX);
    }
  }
}

实验平台是 Leonardo, 实验结果如下:

运行结果

为什么输入的 0xaa 会被判定为 ffffffaa

在  \arduino-1.8.4\hardware\arduino\avr\cores\arduino\CDC.cpp 有如下定义:

int Serial_::read(void)
{
	if (peek_buffer >= 0) {
		int c = peek_buffer;
		peek_buffer = -1;
		return c;
	}
	return USB_Recv(CDC_RX);
}

对于 0xaa 来说,转换为 int 之后的结果就是 ffffffaa,可以用下面的代码来验证:

char c=0xaa;
Serial.println((int)c,HEX);

结果

知道了原因,修改方法也很简单,在变量声明的地方定义为 byte c 即可。

FireBeetle的USB Host Shield

目前市面上最成熟的 USB Host 库当属Oleg Mazurov的USB Host Shield Library 2.0(项目地址 https://github.com/felis/USB_Host_Shield_2.0),几乎可以兼容市面上的所有 Arduino 板,从 Atmel 328P/32U4到 ATMage 2560 再到 EP32都能够使用这个库来驱动 USB 设备。这次我专门为 FireBeetle设计了一个Shield 使得FireBeetle能够驱动一些USB 设备。

首先,依然是硬件设计部分。USB Host Shield Library是基于 MAX3421e实现的,下面可以看作是这个芯片的一个最小系统,需要外接一颗12Mhz 的晶振。此外可以看到这个芯片本身带有GPIO引脚,可以用来做 GPI或者 GPO ,在一些情况下可以弥补主控 GPIO 不足的缺点,但是本次设计并未使用这些引脚。

这个可以看作时 Max4321e 的最小系统了

上述芯片和 FireBeetle接口如下图,二者是通过 SPI 来进行通讯的。下图中IO18/23/19/13 就是 SPI 接口。特别注意,我在板子上预留了 JP3/JP2 这是为以后堆叠使用可能发生的引脚冲突预留的,正常使用的时候 JP1和JP3(称作短接电阻) 是需要焊接起来的;JP2 的功能是预留给某些情况下 USB 设备需要功耗较高,FireBeetle 无法提供时就需要考虑使用外部5V进行供电,此时如果你还使用FIreBeetle 上的 USB端口调试,既有可能出现外部5V 和USB 上面的5V 电压不同的情况。因此,这种情况下需要断开 JP2。

Fire Beetle 端

接下来继续进行硬件设计,绘制PCB。特别注意,下面电路图时有缺陷的,再晶振下面走线,有可能对信号有影响。建议需要的朋友重新设计 PCB。

3D 预览结果:

之后,打板,焊接成品如下,黑色PCB 很 DFRobot 出品的感觉。

接下来就用这个板子配合 FireBeetle来实现一个 USB 键盘转蓝牙的作品。

  1. 为了驱动这个 USB Host Shield 需要对 USB Host Library 做一点修改。
    1. 1 UsbCore.h 中修改为我们使用的SS和INT Pin
#elif defined(ESP32)
//LABZ typedef MAX3421e&lt;P5, P17> MAX3421E; // ESP32 boards
typedef MAX3421e&lt;P13, P26> MAX3421E; // ESP32 boards  // LABZ
#else
  1. 2 usbhost.h 中修改为我们使用的 SPI
#elif defined(ESP32)
//LABZ typedef SPi&lt; P18, P23, P19, P5 > spi;
typedef SPi&lt; P18, P23, P19, P13 > spi; // LABZ
#else

2. 蓝牙键盘功能的实现。这里我们需要用到 ESP32 BLE Keyboard library,在 https://github.com/T-vK/ESP32-BLE-Keyboard 可以找到这个项目。

3.最终的代码如下:

#include <hidboot.h>
#include <SPI.h>

#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

class KbdRptParser : public KeyboardReportParser
{
    void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

void KbdRptParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)
{

  if (bleKeyboard.isConnected()) {
    KeyReport _keyReport;
    _keyReport.modifiers=buf[0];
    for (int i = 2; i < 8; i++) {
      _keyReport.keys[i-2] = buf[i];
    }

    bleKeyboard.sendReport(&_keyReport);
  }
  Serial.print("Received ");  Serial.println(len);
  for (int i = 0; i < len; i++) {
    Serial.print(buf[i]);
    Serial.print("  ");
  }
  Serial.println("  ");
}

USB     Usb;

HIDBoot<USB_HID_PROTOCOL_KEYBOARD>    HidKeyboard(&Usb);

KbdRptParser Prs;

void setup()
{
  Serial.begin( 115200 );
  Serial.println("Start");

  if (Usb.Init() == -1)
    Serial.println("OSC did not start.");

  bleKeyboard.begin();
  delay( 200 );

  HidKeyboard.SetReportParser(0, &Prs);
}

void loop()
{
  Usb.Task();
}

简单的说上电之后,在PC上搜索蓝牙设备,配对之后就可以通过 USB Host Shield 解析键盘数据,然后将这个数据直接填写到蓝牙键盘的结构体中,FIreBeetle 随即将这个信息传递给 PC,PC上就能收到你的按键信息了。

使用一个小键盘测试

电路图文件和PCB 在这里下载:

工作的视频可以在 B站看到

https://www.bilibili.com/video/BV1zy4y1W7oh

VC 运行 PowerShell 命令的方法

有时候,我们需要执行 PowerShell 命令来取得一些信息,通过下面的代码能在 VC 中执行命令并且获得返回值:

#include "stdafx.h"
#include <stdio.h>
#include <string.h>

// 描述:execmd函数执行命令,并将结果存储到result字符串数组中
// 参数:cmd表示要执行的命令,  result是执行的结果存储的字符串数组
// 函数执行成功返回1,失败返回0
#pragma warning(disable:4996)
int execmd(char* cmd, char* result) {
	char buffer[128]; //定义缓冲区
	FILE* pipe = _popen(cmd, "r"); //打开管道,并执行命令
	if (!pipe)
		return 0; //返回0表示运行失败
	while (!feof(pipe)) {
		if (fgets(buffer, 128, pipe)) { //将管道输出到result中
			strcat(result, buffer);
		}
	}
	_pclose(pipe); //关闭管道
	return 1; //返回1表示运行成功
}

int main(void)
{
	char res[2048] = { 0 };
	execmd("powershell $psversiontable", res);
	printf("Result [%s]\r\n",res);
	getchar();
}

下面是运行结果,可以看到二者完全相同。

代码运行结果和 Power Shell 运行结果相同

特别注意:如果遇到下面这样的错误提示,那么需要扩大 res[] 。

Run-Time Check Failure #2 – Stack around the variable ‘a’ was corrupted.

参考:

1.https://www.cnblogs.com/htj10/p/13830785.html VC执行Cmd命令,并获取结果

2.https://blog.csdn.net/weixin_42395980/article/details/112123690 c++ 调用 powershell_十九,Powershell基础入门及常见用法(一)

ESP32/S2 不推荐使用的GPIO Pin

ESP32 不建议使用的 GPIO 如下

  1. IO0 Strapping,用于选择 SPI 启动还是下载启动
  2. IO1 TXD , 用于串口下载
  3. IO2 Strapping,下载启动需要用
  4. IO3 RXD,用于串口下载
  5. IO5 Strapping, SDIO 从机信号输入输出时序
  6. IO6-11 内部 SPI FLASH
  7. IO12 Strapping, MTDI信号
  8. IO15 Strapping MTDO信号

ESP32S2 不建议使用的 GPIO 如下(这个是我自己总结的,缺少佐证):

  1. GPIO0 系统启动模式选择
  2. GPIO1 TXD 用于串口下载
  3. GPIO3 RXD 用于串口下载
  4. GPIO43 TXD 用于串口下载
  5. GPIO44 RXD 用于串口下载
  6. GPIO37 SPI NOR/PSRAM 的SPIHD
  7. IO39 SPI NOR的SPICS0
  8. 6.IO40 SPI NOR/PSRAM 的SPICLK
  9. IO41 SPI NOR/PSRAM 的SPIQ
  10. IO42 SPI NOR/PSRAM 的SPID
  11. IO38 SPI NOR/PSRAM 的 SPIWP
  12. IO26 用作PSRAM SPICS1
  13. IO27 用作PSRAM SPIHD
  14. IO28用作PSRAM SPIWP
  15. IO29 用作PSRAM SPICS0
  16. IO30 用作PSRAM SPICLK
  17. IO31 用作PSRAM SPIQ
  18. IO32 用作PSRAM SPID
  19. GPIO45 VDD_SPI 电压选择
  20. GPIO46 系统启动是否打印 ROM Code

关于 S2 的 PSRAM 在【参考3】有描述:

特别提醒:如果你使用开发板,例如 S2 Saola 这种, IO18 上有LED,如果直接使用这个pin作为SPI CLOCK可能会导致不断重启的问题。

参考:

1.http://www.360doc.com/content/20/0312/20/42387867_898729516.shtml

2.ESP32­WROOM­32D &ESP32­WROOM­32U 技术规格书

3.https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_cn.pdf

====================================================

2023年1月17日更新

ESP32S3不建议使用的 GPIO 如下:

1.GPIO0 Strapping
2.GPIO3 Strapping
3.GPIO19 USB-JTAG
4.GPIO20 USB-JTAG
5.GPIO26-37 SPI0/1
6.GPIO45 Strapping
7.GPIO46 Strapping

  • Strapping 管脚:GPIO2、GPIO3、GPIO45 和 GPIO46 是 Strapping 管脚。更多信息请参考 ESP32-S3 技术规格书。
  • SPI0/1:GPIO26-32 通常用于 SPI flash 和 PSRAM,不推荐用于其他用途。当使用八线 flash 或八线 PSRAM 或同时使用两者时,GPIO33~37 会连接到 SPIIO4 ~ SPIIO7 和 SPIDQS。因此,对于内嵌 ESP32-S3R8 或 ESP32-S3R8V 芯片的开发板,GPIO33~37 也不推荐用于其他用途。
  • USB-JTAG:GPIO19 和 GPIO20 默认用于 USB-JTAG。用做 GPIO 时驱动程序将禁用 USB-JTAG。

上述来自:https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-reference/peripherals/gpio.html