文章目录
- 目的
- 数字IO口
- 基本使用
- 外部中断
- 使用示例
- 参考链接
- LEDC(PWM)
- 常用方法
- 使用示例
- 参考链接
- SigmaDelta
- 参考链接
- ADC
- 常用方法
- 使用示例
- 参考链接
- 存在的问题
- DAC
- 常用方法
- 使用示例
- 参考链接
- Serial port
- I2C
- 参考链接
- I2S
- 参考链接
- SPI
- 参考链接
- CAN
- 参考链接
- 触摸功能
- 参考链接
- HallSensor
- 参考链接
- 题外话(全局中断关闭与开启)
- 总结
目的
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));
}
参考链接
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/blob/master/cores/esp32/
/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:///content/17/1231/01/47685146_717797647.shtml
/sns/?s=/news/index/detail/id/ -
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()
{
}
参考链接
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/AnalogOut/LEDCSoftwareFade
/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/AnalogOut/ledcWrite_RGB
SigmaDelta
SigmaDelta一般用在红外遥控器上,常见的家电的遥控器都是使用这类信号的,这里仅做下记录。
参考链接
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/blob/master/cores/esp32/
/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部分使用示例;
参考链接
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/blob/master/cores/esp32/
存在的问题
- 当使用WiFi的时候无法使用ADC2;
- ESP32的ADC线性度挺差,比如输入衰减为11dB时只能保证在150–2450mV间测量较准确(详见ESP32技术规格书);
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()
{
}
参考链接
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/blob/master/cores/esp32/
Serial port
Serial port使用见之前文章:
《使用Arduino开发ESP32(02):串口(Serial port)使用说明》
I2C
I2C是一种常用的接口,这里先做下记录。
参考链接
/espressif/arduino-esp32/tree/master/libraries/Wire
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/blob/master/cores/esp32/
I2S
I2S相对来说用的不多,这里仅做下记录。
参考链接
/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/I2S
/espressif/arduino-esp32/blob/master/tools/sdk/include/driver/driver/
SPI
SPI是一种常用的接口,这里先做下记录。
参考链接
/espressif/arduino-esp32/tree/master/libraries/SPI
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/blob/master/cores/esp32/
CAN
CAN是一种常用的现场总线,这里仅做下记录。
参考链接
/espressif/arduino-esp32/blob/master/tools/sdk/include/driver/driver/
触摸功能
触摸功能主要用于触摸传感器,像是按键、滑块等。这里先做下记录。
《ESP32 触摸传感器应用方案简介》
《ESP32 触摸功能开发套件》
参考链接
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/blob/master/cores/esp32/
/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/Touch
HallSensor
霍尔传感器相对来说用的不多,这里仅做下记录。(ESP32的霍尔传感器读取其实就是靠ADC的,可以参考ADC部分)
参考链接
/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/HallSensor
题外话(全局中断关闭与开启)
使用IO口时很多操作对时序要求比较高,如果系统频繁进入中断可能会使操作失败,这时候就需要暂时关闭中断等操作完成后再开启中断,可以使用下面方法进行中断的开启和关闭:
-
noInterrupts()
cli()
关闭中断; -
interrupts()
sei()
开启中断;
注意:关闭中断完成需要的操作后一定要记得开启中断!
总结
事实上对于单片机来说IO口与相关外设的使用才是大头的内容,一篇文章难以介绍详尽,这是只是做个记录。不过相应的各种类别的单片机IO口与相关外设使用起来大同小异,网上教程资源比较充分,如果对某块功能不了解的话可以参考别的更加详细的教程。