Arduino core for the ESP32中IO口和外設的使用和一般的Arduino產不多,這裏做個說明與記錄。
先上一張ESP32模塊的管腳圖(點擊看大圖):
更完整管腳說明需要去參考的樂鑫官方《ESP32 技術規格書》。
數字IO口
基本使用
IO口基本使用方式如下:
使用pinMode(pin, mode)來設置GPIO口工作模式,mode可選比較多INPUT、OUTPUT、INPUT_PULLUP、INPUT_PULLDOWN模式(輸入、輸出、上拉輸入、下拉輸入,另外還有開漏等模式),具體是否能設置對應模式還得參考技術規格書(一般的GPIO0 ~ 33可以設置為輸出,基本上都可以設置為輸入,GPIO6 ~ 11一般不推薦使用,因為這幾個口接了存儲程序用的Flash,不當使用可能引起程序崩潰);
使用digitalWrite(pin, value)來設置輸出狀態,value可選值為HIGH或LOW,即1和0;
使用digitalRead(pin)來讀取GPIO口電平,返回值為HIGH或LOW,即1和0;
題外話:請注意ESP32的IO12,這個IO口上上電時的電平會決定外部flash(存放程序的那顆)的工作電壓,上電時該腳為高則認為flash工作於1.8V,為低則認為flash工作於3.3V。常用的像是Wroom-32係列模塊該腳內部已下拉,即flash是工作於3.3V的,若外部電路接強上拉則可能導致模塊工作異常。
外部中斷
外部中斷使用方式如下:
使用attachInterrupt(uint8_t pin, void (*)(void), int mode)或attachInterruptArg(uint8_t pin, void (*)(void*), void * arg, int mode)來設置外部中斷,輸入參數有gpio號、中斷觸發時的回調函數、回調函數輸入參數、外部中斷觸發模式(RISING、FALLING、CHANGE……上升沿、下降沿、改變時、低電平、高電平等);
使用detachInterrupt(uint8_t pin)來關閉外部中斷;
使用示例
使用下麵代碼進行測試:
// IO14 輸出
// IO12 下拉輸入模式 電平改變觸發中斷
// 使用導線連接 IO14 和 IO12
void callBack(void)
{
int lv = digitalRead(12); //讀取加載到IO12上的電平
Serial.printf("觸發了中斷,當前電平是: %d\n", lv);
}
void setup()
{
Serial.begin(115200);
Serial.println();
pinMode(14, OUTPUT);
digitalWrite(14, LOW);
pinMode(12, INPUT_PULLDOWN);
attachInterrupt(12, callBack, CHANGE); //使能中斷
for (int i = 0; i < 5; i++)
{
delay(1000);
digitalWrite(14, 1 ^ digitalRead(14)); //翻轉 IO14 輸出電平
}
detachInterrupt(12); //失能中斷
}
void loop()
{
delay(1000);
digitalWrite(14, 1 ^ digitalRead(14));
}
參考鏈接
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-gpio.h
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-gpio.c
https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/GPIO
LEDC(PWM)
Arduino core for the ESP32並沒有一般Arduino中用來輸出PWM的analogWrite(pin, value)方法,取而代之的ESP32有一個LEDC,設計是用來控製LED,像是實現呼吸燈或是控製全彩LED之類,簡單的輸出PWM當然不在話下。
ESP32的LEDC總共有16個路通道(0 ~ 15),分為高低速兩組,高速通道(0 ~ 7)由80MHz時鍾驅動,低速通道(8 ~ 15)由1MHz時鍾驅動。
常用方法
double ledcSetup(uint8_t channel, double freq, uint8_t resolution_bits)
設置LEDC通道對應的頻率和計數位數(占空比分辨率),各項說明如下:
channel為通道號,取值0 ~ 15;
freq期望設置頻率;
resolution_bits計數位數,取值0 ~ 20(該值決定後麵ledcWrite方法中占空比可寫值,比如該值寫10,則占空比最大可寫1023 即 (1<<resolution_bits)-1 );
通道最終頻率 = 時鍾 / ( 分頻係數 * ( 1 << 計數位數 ) );(分頻係數最大為1024)
該方法返回最終頻率;
void ledcWrite(uint8_t channel, uint32_t duty)
指定通道輸出一定占空比波形;
double ledcWriteTone(uint8_t channel, double freq)
類似於arduino的tone,當外接無源蜂鳴器的時候可以發出某個聲音(根據頻率不同而不同);
double ledcWriteNote(uint8_t channel, note_t note, uint8_t octave)
該方法是上麵方法的進一步封裝,可以直接輸出指定調式和音階聲音的信號,參數如下:
note:調式,相當於do、re、mi、fa……這些,取值為NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G, NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B
octave音階,取值0~7;
樂理相關內容可以參考下麵文章:
http://www.360doc.com/content/17/1231/01/47685146_717797647.shtml
https://www.musicbody.net/sns/index.php?s=/news/index/detail/id/406.html
uint32_t ledcRead(uint8_t channel)
返回指定通道占空比的值;
double ledcReadFreq(uint8_t channel)
返回指定通道當前頻率(如果當前占空比為0 則該方法返回0);
void ledcAttachPin(uint8_t pin, uint8_t channel)
將LEDC通道綁定到指定IO口上以實現輸出;
void ledcDetachPin(uint8_t pin)
解除IO口的LEDC功能;
使用示例
使用下麵代碼進行測試:
// IO14 輸出PWM
// IO12 讀取IO14輸出的信號
void setup()
{
Serial.begin(115200);
Serial.println();
ledcSetup(8, 1, 10); //設置LEDC通道8頻率為1,分辨率為10位,即占空比可選0~1023
ledcAttachPin(14, 8); //設置LEDC通道8在IO14上輸出
pinMode(12, INPUT_PULLDOWN);
for (int i = 0; i < 5; i++)
{
ledcWrite(8, 250 * i); //設置輸出PWM
for (int j = 0; j < 100; j++)
{
delay(10);
Serial.println(digitalRead(12));
}
}
}
void loop()
{
}
參考鏈接
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-ledc.h
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-ledc.c
https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/AnalogOut/LEDCSoftwareFade
https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/AnalogOut/ledcWrite_RGB
SigmaDelta
SigmaDelta一般用在紅外遙控器上,常見的家電的遙控器都是使用這類信號的,這裏僅做下記錄。
參考鏈接
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-sigmadelta.h
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-sigmadelta.c
https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/AnalogOut/SigmaDelta
ADC
ADC是比較常用的功能,使用起來也比較簡單。ESP32有兩個ADC,每個ADC有多個通道,同一時間每個ADC隻能采集一個通道。
常用方法
uint16_t analogRead(uint8_t pin)
獲取指定IO口的模擬電壓數據(該方法將阻塞直到采集完成);
void analogReadResolution(uint8_t bits)
設置模擬數據讀取分辨率,取值1~16,默認為12;
void analogSetWidth(uint8_t bits)
設置ADC采樣分辨率,取值9~12,默認為12;
void analogSetCycles(uint8_t cycles)
設置單次采樣的周期,取值1~255,默認為8;
void analogSetSamples(uint8_t samples)
設置單次采樣的實際采樣次數,取值1~255,默認為1;
該項的設置相當於提高了ADC的靈敏度,比如該值為2,則采樣獲得數據就是真實數據的2倍;
void analogSetClockDiv(uint8_t clockDiv)
設置ADC時鍾分頻係數,取值1~255,默認為1;
void analogSetAttenuation(adc_attenuation_t attenuation)
設置ADC全局輸入衰減,取值ADC_0db, ADC_2_5db, ADC_6db, ADC_11db,默認為11db;
當 VDD_A 為 3.3V 時:
0dB 下量程最大為 1.1V
2.5dB 下量程最大為 1.5V
6dB 下量程最大為 2.2V
11dB 下量程最大為 3.9V(最大可以采集到3.3V電壓)
void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation)
設置單獨某個IO口的輸入衰減;
int hallRead()
Get value for HALL sensor (without LNA) connected to pins 36(SVP) and 39(SVN);
(關聯下文霍爾傳感器)
========= 以下為非阻塞采樣 =========
bool adcAttachPin(uint8_t pin)
將IO口連接到ADC;
bool adcStart(uint8_t pin)
開啟采樣與轉換;
bool adcBusy(uint8_t pin)
檢查采樣與轉換是否完成;
uint16_t adcEnd(uint8_t pin)
讀取采集到的數據(如果未完成將阻塞至完成);
使用示例
見下文DAC部分使用示例;
參考鏈接
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-adc.h
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-adc.c
DAC
相對於ADC來說DAC用的稍微少些,不過用起來也不複雜。
常用方法
void dacWrite(uint8_t pin, uint8_t value)
DAC輸出;
pin取值為25、26;
value取值為0~255;
DAC輸出電壓由VDD3P3_RTC引腳輸入電壓和value決定,使用常用的模塊時VDD3P3_RTC上電壓為3.3V;
DAC_OUT = VDD3P3_RTC * value / 256 ;
使用示例
使用下麵代碼進行測試:
void setup()
{
Serial.begin(115200);
Serial.println();
dacWrite(26, 100); //IO26 DAC輸出 100*3.3V/255≈1.294V
int vtmp = analogRead(27); //IO27 ADC獲取電壓
Serial.printf("采樣值為:%d\n", vtmp);
Serial.printf("電壓為:%.3fV\n", vtmp * 3.9 / 4095);
}
void loop()
{
}
參考鏈接
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-dac.h
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-dac.c
Serial port
Serial port使用見之前文章:
《使用Arduino開發ESP32(02):串口(Serial port)使用說明》
I2C
I2C是一種常用的接口,這裏先做下記錄。
參考鏈接
https://github.com/espressif/arduino-esp32/tree/master/libraries/Wire
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-i2c.h
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-i2c.c
I2S
I2S相對來說用的不多,這裏僅做下記錄。
參考鏈接
https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/I2S
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/driver/driver/i2s.h
SPI
SPI是一種常用的接口,這裏先做下記錄。
參考鏈接
https://github.com/espressif/arduino-esp32/tree/master/libraries/SPI
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-spi.h
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-spi.c
CAN
CAN是一種常用的現場總線,這裏僅做下記錄。
參考鏈接
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/driver/driver/can.h
觸摸功能
觸摸功能主要用於觸摸傳感器,像是按鍵、滑塊等。這裏先做下記錄。
《ESP32 觸摸傳感器應用方案簡介》
《ESP32 觸摸功能開發套件》
參考鏈接
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-touch.h
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-touch.c
https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/Touch
HallSensor
霍爾傳感器相對來說用的不多,這裏僅做下記錄。(ESP32的霍爾傳感器讀取其實就是靠ADC的,可以參考ADC部分)
參考鏈接
https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/HallSensor
題外話(全局中斷關閉與開啟)
使用IO口時很多操作對時序要求比較高,如果係統頻繁進入中斷可能會使操作失敗,這時候就需要暫時關閉中斷等操作完成後再開啟中斷,可以使用下麵方法進行中斷的開啟和關閉:
noInterrupts()
cli()
關閉中斷;
interrupts()
sei()
開啟中斷;
注意:關閉中斷完成需要的操作後一定要記得開啟中斷!
總結
事實上對於單片機來說IO口與相關外設的使用才是大頭的內容,一篇文章難以介紹詳盡,這是隻是做個記錄。不過相應的各種類別的單片機IO口與相關外設使用起來大同小異,網上教程資源比較充分,如果對某塊功能不了解的話可以參考別的更加詳細的教程。