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

按下ENABLE,會重啟ESP32。
而當常壓BOOT以及按下ENABLE,ESP32會重啟編輯模式(Programming mode)
## Specification
* ESP32為雙核心,意味著可以同時處理兩件事(但需要在programe中激活)
* 可以執行32 bit的程式
* 時脈(Clock frequency):240MHz
* 記憶體(RAM):512 kB
* 30隻腳位(也有30隻腳位的版本)

## 各個開發板的比較
| 類型 | 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即可。

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