---
# System prepended metadata

title: ESP32 學習筆記

---

# ESP32 學習筆記

# Introduction the ESP32 Board
ESP32是一系列由中國上海樂鑫科技(Espressif)推出的晶片、模組、開發板，採用Tensilica Xtensa LX6微處理器，包括雙核心和單核變體，支援Arduino開放性架構，內建WiFi及低功率藍芽，可用腳位也擴增到約26根，大部分腳位同時支援類比及數位，內建霍爾、溫度、觸控感測器，主頻高達240mHz的雙核心CPU，傳輸界面SPI、I2C、UART等強大的功能。

## Features
* ESP32本身帶有WiFi與藍芽的功能
* 只能吃與輸出DC 3.3V
* 板子上有兩個按鈕，ENABLE與BOOT，
![](https://i.imgur.com/LE4x0xm.jpg)
按下ENABLE，會重啟ESP32。
而當常壓BOOT以及按下ENABLE，ESP32會重啟編輯模式(Programming mode)

## Specification
* ESP32為雙核心，意味著可以同時處理兩件事(但需要在programe中激活)
* 可以執行32 bit的程式
* 時脈(Clock frequency)：240MHz
* 記憶體(RAM)：512 kB
* 30隻腳位(也有30隻腳位的版本)
![](https://i.imgur.com/hgrJIuX.jpg)
## 各個開發板的比較
| 類型 | Arduino UNO | Arduino Mega | STM32 | ESP32 |
| --------| -------- | -------- | 
| Text     | Text     | Text     |

## Arduino IDE添加ESP32開發板
點選File，選擇Prefences，在Additional boards manager URLS輸入https://dl.espressif.com/dl/package_esp32_index.json即可。
![](https://i.imgur.com/US7tgFC.png)

## 利用ESP32來計算馬達轉速
由於馬達的Encoder頻率太高，導致普通的Arduino在使用中斷函式時，會出現其他程式無法使用的窘境，因此需要使用ESP32的雙核心分區計算。


## 遭遇問題
###  **<font color="bull">系統重啟</font>**
``` Arduino
#define PWM1 13
#define INA1 12
#define INB1 14
#define Encoder_A 32
#define Encoder_B 33

hw_timer_t *timer = NULL; // 建立計時器物件
/* 宣告任務變數 */
TaskHandle_t Task_Motorcontrol;
TaskHandle_t Task_Encoder;

volatile long Encoder_R;
int Vel_Right;
unsigned long RightMotor_Speed;

void EncoderISR(){
  sei();
  Vel_Right = Encoder_R;
  RightMotor_Speed = Vel_Right / 13 * 60;
  Encoder_R = 0;
  Serial.print("Right motor:");
  Serial.println(RightMotor_Speed);
}

void READ_ENCODER_R(){
  if(digitalRead(Encoder_A) == LOW){
    if(digitalRead(Encoder_B) == LOW)
      Encoder_R++;
    else
      Encoder_R--;
  } else{
    if(digitalRead(Encoder_B) == LOW)
      Encoder_R--;
    else
      Encoder_R++;
  }
}

void Motor_control(void * pvParameters){
  Serial.print("Task_Motorcontrol running on core");
  Serial.println(xPortGetCoreID());
  int i;
  for(;;){
    digitalWrite(INA1, LOW);
    digitalWrite(INB1, HIGH);

    for(i=80 ; i<=255 ; i++){
      ledcWrite(0, i);
      // Serial.print("Motor PWM: ");
      // Serial.println(i);
      delay(2000);
    }
  }
}

void Encoder_process(void * pvParameters){
  Serial.print("Task_Encoder running on core");
  Serial.println(xPortGetCoreID());
  for(;;){
    attachInterrupt(Encoder_A, READ_ENCODER_R, CHANGE);
    timer = timerBegin(0, 80, true);
    timerAttachInterrupt(timer, &EncoderISR, true);
    timerAlarmWrite(timer, 5000, true);
    timerAlarmEnable(timer);
  }
}

void setup() {
  Serial.begin(115200);
  setCpuFrequencyMhz(240);

  pinMode(PWM1, OUTPUT);
  pinMode(INA1, OUTPUT);
  pinMode(INB1, OUTPUT);
  pinMode(Encoder_A, INPUT);
  pinMode(Encoder_B, INPUT);
  delay(200);

  digitalWrite(INA1, HIGH);
  digitalWrite(INB1, HIGH);
  digitalWrite(PWM1, LOW);
  delay(100);
  ledcSetup(0, 5000, 8);
  ledcAttachPin(PWM1, 0);

  xTaskCreatePinnedToCore(Motor_control, "Motor Control", 10000, NULL, 1, &Task_Motorcontrol, 1);
  delay(500);
  xTaskCreatePinnedToCore(Encoder_process, "Encoder", 10000, NULL, 1, &Task_Encoder, 0);
  delay(500);
}

void loop() {

}
```
Solution：
1. 在 EncoderISR() 函數中，不應該使用 sei()，因為該函數已經在中斷上下文中調用。sei() 用於允許中斷，而在中斷上下文中，中斷是已經被允許的。因此，可以直接刪除 sei()。

3. 在 Motor_control() 函數中，應該使用 vTaskDelay() 函數來替代 delay() 函數，因為 delay() 函數會阻塞程式執行，導致其他任務無法執行，進而導致系統不穩定。可以將 delay(2000) 替換為 vTaskDelay(pdMS_TO_TICKS(2000))。

5. 在 Encoder_process() 函數中，由於一直進入中斷處理函數，可能會導致中斷佔滿 CPU 資源，從而導致系統不穩定。建議使用 Timer 來設定定時器中斷，而不是在 for(;;) 迴圈中重複註冊中斷。

### **<font color="bull">核心恐慌</font>**
``` arduino
#define PWM1 13
#define INA1 12
#define INB1 14
#define Encoder_A 32
#define Encoder_B 33

hw_timer_t *timer = NULL; // 建立計時器物件
/* 宣告任務變數 */
TaskHandle_t Task_Motorcontrol;

volatile long Encoder_R, Vel_Right, Vel_Left;
long RightMotor_RPM, LeftMotor_RPM;
int PPR = 13;               // Encoder 1圈pulse數
int interval = 1000;       // 計數時間 unit:ms


void Motor_control(void * pvParameters){
  Serial.print("Task_Motorcontrol running on core");
  Serial.println(xPortGetCoreID());
  int i;
  for(;;){
    digitalWrite(INA1, LOW);
    digitalWrite(INB1, HIGH);
    // for(i=80 ; i<=255 ; i++){
      ledcWrite(0, 255);
      // Serial.print("Motor PWM: ");
      // Serial.println(i);
      vTaskDelay(200);
    // }
  }
}

void setup() {
  Serial.begin(115200);
  setCpuFrequencyMhz(240);

  pinMode(PWM1, OUTPUT);
  pinMode(INA1, OUTPUT);
  pinMode(INB1, OUTPUT);
  pinMode(Encoder_A, INPUT);
  pinMode(Encoder_B, INPUT);
  delay(200);

  digitalWrite(INA1, HIGH);
  digitalWrite(INB1, HIGH);
  digitalWrite(PWM1, LOW);
  delay(100);
  ledcSetup(0, 5000, 8);
  ledcAttachPin(PWM1, 0);

  xTaskCreatePinnedToCore(Motor_control, "Motor Control", 10000, NULL, 1, &Task_Motorcontrol, 1);
  delay(500);

  attachInterrupt(Encoder_A, READ_ENCODER_R, CHANGE);
  timer = timerBegin(0, 80, true);                  // timerBegin(定時器編號, 分頻數, 是否是累加模式)
  timerAttachInterrupt(timer, &EncoderISR, true);
  timerAlarmWrite(timer, 5000, true);           // timerAlarmWrite(目標定時器, 計數上限, 是否重裝載) 第二個參數單位(us)
  timerAlarmEnable(timer);
}

void loop() {
  Serial.print("Encoder count:");
  Serial.print(Vel_Right);
  Serial.print(" ,Right motor:");
  Serial.println(RightMotor_RPM);
  vTaskDelay(200);
}
```
```
ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:10124
load:0x40080400,len:5856
entry 0x400806a8
Task_Motorcontrol running on core1
Encoder count:0 ,Right motor:0
Encoder count:0 ,Right motor:0
Encoder count:0 ,Right motor:0
Encoder count:0 ,Right motor:0
Encoder count:0 ,Right motor:0
Encoder count:0 ,Right motor:0
Guru Meditation Error: Core  1 panic'ed (Coprocessor exception)
Core 1 register dump:
PC      : 0x400d0ca3  PS      : 0x00060031  A0      : 0x80081270  A1      : 0x3ffbea80  
A2      : 0x3ffbdc5c  A3      : 0x00000001  A4      : 0x00000400  A5      : 0x00000000  
A6      : 0x00000000  A7      : 0x00000000  A8      : 0x00000000  A9      : 0x00000000  
A10     : 0x00000000  A11     : 0x3ffbfe90  A12     : 0x00000020  A13     : 0x80000020  
A14     : 0x00000003  A15     : 0x00000000  SAR     : 0x00000012  EXCCAUSE: 0x00000004  
EXCVADDR: 0x00000000  LBEG    : 0x00000000  LEND    : 0x00000000  LCOUNT  : 0x00000000  
Core 1 was running in ISR context:
EPC1    : 0x400d0ca3  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x40083ecb

ELF file SHA256: 0000000000000000

Backtrace: 0x400d0ca3:0x3ffbea80 0x4008126d:0x3ffbeb60 0x40083cb1:0x3ffbeb80 0x400e9a47:0x3ffbc6b0 0x400e1dfb:0x3ffbc6d0 0x40087a4e:0x3ffbc6f0 0x400865b9:0x3ffbc710

Rebooting...
```
Solution：
```timerAlarmWrite()```函式的第二個參數修正

### **<font color="bull">無法燒入program</font>**
```
Sketch uses 208046 bytes (15%) of program storage space. Maximum is 1310720 bytes.
Global variables use 14016 bytes (4%) of dynamic memory, leaving 313664 bytes for local variables. Maximum is 327680 bytes.
esptool.py v3.0-dev
Serial port COM5
Traceback (most recent call last):
  File "esptool.py", line 3682, in <module>
  File "esptool.py", line 3675, in _main
  File "esptool.py", line 3329, in main
  File "esptool.py", line 263, in __init__
  File "site-packages\serial\__init__.py", line 88, in serial_for_url
  File "site-packages\serial\serialwin32.py", line 62, in open
serial.serialutil.SerialException: could not open port 'COM5': WindowsError(5, '\xa6s\xa8\xfa\xb3Q\xa9\xda\xa1C')
Failed to execute script esptool
Failed uploading: uploading error: exit status 0xffffffff
```