玩转 ESP32 + Arduino (三) GPIO和串口

我用的 ESP32 DEVKIT V1
引脚图



原理图


【腾讯文档】ESP32引脚功能筛选器https://docs.qq.com/sheet/DWXpUWVFMZWlQZGl4

一、GPIO

1、初始化GPIO

pinMode(pin, mode)

作用:设置一个引脚(pin)作为GPIO时的I/O模式。

参数:

pin:引脚编号

mode:GPIO的I/O模式,取值有3种

INPUT :作为数字输入

OUTPUT :作为数字输出

INPUT_PULLUP:作为数字输入,且使能引脚的内部上拉电阻

注意:

a、引脚作为PWM输出时,无需先前使用pinMode配置其模式,因为PWM本身就代表了输出的意思。且官方库源代码中可以发现,它已经帮我们配置为输出了。

b、模拟引脚也可以作为数字引脚使用,完全OK。这在数字引脚不够用的情况下非常有用。

2、GPIO数字输出

digitalWrite(pin,value)

作用:设置一个数字输出引脚的输出电平值,HIGH或者LOW。

参数:

pin:引脚编号。此引脚必须在之前使用pinMode设置为OUTPUT模式。

value:2个值

LOW:输出低电平

HIGH:输出高电平

3、GPIO数字输入

int digitalRead(pin)

作用:读取一个数字输入引脚的电平值。

返回:HIGH(高电平)或者LOW(低电平)。

参数:

pin:引脚编号。

示例 : 按键控制灯亮灭

void setup()
{
  pinMode(2,OUTPUT);
  pinMode(0,INPUT_PULLUP);
}

void loop()
{
  if(digitalRead(0))
  {
    while(digitalRead(0));
    digitalWrite(2,!digitalRead(2));
  }
}

4、GPIO模拟输入:analogRead(pin)

注意 : 模拟输入是通过PWM实现的, 所以不需要初始化PIN

analogRead(4)

5. 模拟信号输入分辨率: analogSetWidth(bit);

bit的值 范围
9 0~511
10 0~1023
11 0~2047
12(默认) 0~4095

6. 模拟信号输出 (基于LEDC)

LEDC是基于PWM调制实现模拟输出的.
与arduino uno主板不同, ESP32的PWM模拟是一个个通道 共16个, 通道可以映射到引脚上. 引脚就可以输出PWM信号了.

6.1 设置通道 ledcSetup(channel,freq,bit_num)

参数:

  • channel : LEDC的PWM通道参数,可选0~15
  • freq : 10Hz到40MHz , 但较高的频率精确度低
  • bit_num: 占空比分辨率(可选1~16), 比如bit_num=8 则范围 0~2的8次方 , 也就是0~255

推荐的配置:

频率 位深 过渡的可用步骤
1220赫兹 16 65536
2441赫兹 15 32768
4882赫兹 14 16384
9765Hz 13 8192
19531赫兹 12 4096
ledcSetup(1,1200,16);
6.2 通道与引脚映射 ledcAttachPin(pin,channel)
  ledcAttachPin(5,1);

注意: 一个通道可以同时映射多个引脚

6.3 取消引脚的PWM映射 ledcDetachPin(pin)
ledcDetachPin(5);
6.4 向指定通道写入占空比 ledcWrite(channel,duty)

例: 呼吸灯

bool add_status = true;
void setup()
{
  pinMode(2,OUTPUT);
  ledcSetup(2,1200,8);
  ledcAttachPin(2,2);
}

void loop()
{
   for(int i = 0 ; i<256; i++)
   {
    if(add_status)
    {
      ledcWrite(2,i);
    }
    else
    {
      ledcWrite(2,256-i);
    }
    delay(5);
  }
  add_status = !add_status;
}
例子, 全彩呼吸灯
#include <Arduino.h>

#define LED_R 27
#define LED_G 33
#define LED_B 32

void setup()
{
  Serial.begin(115200);
  ledcSetup(1, 1200, 8);
  ledcSetup(2, 1200, 8);
  ledcSetup(3, 1200, 8);

  ledcAttachPin(LED_R, 1);
  ledcAttachPin(LED_G, 2);
  ledcAttachPin(LED_B, 3);

  ledcWrite(1, 255);
  ledcWrite(2, 255);
  ledcWrite(3, 255);
}

void loop()
{
  for (int i = 0; i < 510; i++)
  {
    if (i >= 0 && i < 255)
      ledcWrite(1, 255 - i);
    if (i >= 255 && i < 510)
      ledcWrite(1, i - 255);

    if (i >= 0 && i < 170)
      ledcWrite(2, 85 + i);
    if (i >= 170 && i < 425)
      ledcWrite(2, 425 - i);
    if (i >= 425 && i < 510)
      ledcWrite(2, i - 425);

    if (i >= 0 && i < 85)
      ledcWrite(3, 85 - i);
    if (i >= 85 && i < 340)
      ledcWrite(3, i - 85);
    if (i >= 340 && i < 510)
      ledcWrite(3, 595 - i);
    delay(10);
  }
}
6.5 向指定通道输出指定频率的音符信号 ledcWriteTone(channel, freq)
6.6 向指定通道输出指定的音符和音阶 ledcWriteNote(channel,note,octava)

参数

  • note : 音符 可选(NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_C......)
  • octava : 音阶 , 可选0~7

7. 模拟信号输出函数 (基于DAC)

ESP32提供了两个DAC通道, 对应引脚25 , 26. 精度为8位.

dacWrite(pin,value);

  • value取值: 0~255
void setup()
{

}

void loop()
{
   for(int i = 0 ; i<256; i++)
   {
    dacWrite(25,i);
    delay(10);
  }
}

二、串口打印 UART

ESP32共有3个UART端口, 其中UART1用于Flash读/写.

串口名 Arduino名 TX RX
UART0 Serial pin1 pin3
UART1 Serial1 pin10 pin9
UART2 Serial2 pin17 pin16

1、串口初始化

Serial.begin(speed, config)

  • 参数:
    • speed:波特率,一般取值9600,115200等。
    • config:设置数据位、校验位和停止位。默认SERIAL_8N1表示8个数据位,无校验位,1个停止位。
  • 返回值:无。

2、关闭串口

Serial.end()

  • 描述:禁止串口传输。此时串口Rx和Tx可以作为数字IO引脚使用。
  • 原型:Serial.end()
  • 参数:无。
  • 返回值:无。

3、串口打印

Serial.print()

  • 描述:串口输出数据,写入字符数据到串口。
  • 原型:
    • Serial.print(val)
    • Serial.print(val, format)
  • 参数:
    • val:打印的值,任意数据类型。
    • config:输出的数据格式。BIN(二进制)、OCT(八进制)、DEC(十进制)、HEX(十六进制)。对于浮点数,此参数指定要使用的小数位数。

4、串口输出数据并换行 println() 和 printf()

Serial.println()
Serial.printf()

  • 描述:串口输出数据并换行。
  • 原型:
    • Serial.println(val)
    • Serial.println(val, format)
  • 参数:
    • val:打印的值,任意数据类型。
    • config:输出的数据格式。
  • 返回值:返回写入的字节数。
附: 常用格式字符及转义字符
字符 说明
%o 八进制整数输出
%d 十进制整数输出
%x 十六进制整数输出
%f 浮点输出,默认6位小数
%c 单字符输出
%s 字符串输出
\n 换行
\r 回车
\t Tab制表符

5、将二进制数写入串口

Serial.write()

描述
将二进制数据写入串行端口。该数据以字节或一系列字节的形式发送;要发送代表数字数字的字符,请改用print()函数。
句法
*Serial*.write(val)
*Serial*.write(str)
*Serial*.write(buf, len)
参量
*Serial*:串行端口对象。请参阅“ 串行”主页上每个板的可用串行端口列表。
val:要作为单个字节发送的值。
str:作为一系列字节发送的字符串。
buf:要作为一系列字节发送的数组。
len:要从数组发送的字节数。
退货
write()将返回写入的字节数,尽管读取该数字是可选的。资料类型:size_t

6、判断串口缓冲区的状态

Serial.available()

  • 描述:判断串口缓冲区的状态,返回从串口缓冲区读取的字节数。
  • 原型:Serial.available()
  • 参数:无。
  • 返回值:可读取的字节数。

7、读取串口数据

Serial.read()
描述:读取串口数据,一次读一个字符,读完后删除已读数据。

  • 原型:Serial.read()
  • 参数:无。
  • 返回值:返回串口缓存中第一个可读字节,当没有可读数据时返回-1,整数类型。
#include <Arduino.h>
char rev;
void setup() {
  Serial.begin(115200);
}

void loop() {
  if(Serial.available())
  {
    rev=Serial.read();
    Serial.print("rev=");
    Serial.println(rev);
    }
}

Serial.readBytes()

  • 描述:从串口读取指定长度的字符到缓存数组。
  • 原型:Serial.readBytes(buffer, length)
  • 参数:
    • buffer:缓存变量。
    • length:设定的读取长度。

返回值:返回存入缓存的字符数。

Serial.readString()

描述

Serial.readString()从串行缓冲区读取字符到字符串。如果超时,该函数将终止。

Serial.readString()Stream实用程序类继承。

句法

*Serial*.readString()

参量

*Serial*:串行端口对象。请参阅“ 串行”主页上每个板的可用串行端口列表。

返回值

一个String从串行读缓冲器

Serial.readStringUntil()

描述
readStringUntil()从串行缓冲区读取字符到字符串。如果超时,该函数将终止(请参见setTimeout())。
Serial.readStringUntil()Stream实用程序类继承。
句法
*Serial*.readStringUntil(terminator)
参量
*Serial*:串行端口对象。请参阅“ 串行”主页上每个板的可用串行端口列表。
terminator:要搜索的字符。允许的数据类型:char
返回值
String从串行缓冲区的整个读取,直到终止符

注意和警告
终止符将从串行缓冲区中丢弃。

Serial.find()

描述
Serial.find()从串行缓冲区读取数据,直到找到目标为止。true如果找到目标,函数将返回false超时。
Serial.find()实用程序类继承。
句法
*Serial*.find(target)
*Serial*.find(target, length)
参量
*Serial*:串行端口对象。请参阅“ 串行”主页上每个板的可用串行端口列表。
target:要搜索的字符串。允许的数据类型:char
length:目标的长度。允许的数据类型:size_t
返回值
资料类型:bool

Serial.findUntil()

描述
Serial.findUntil() 从串行缓冲区读取数据,直到找到给定长度的目标字符串或终止符字符串。
如果找到目标字符串,则该函数返回true;如果超时,则返回false。
Serial.findUntil()Stream实用程序类继承。
句法
*Serial*.findUntil(target, terminal)
参量
*Serial*:串行端口对象。请参阅“ 串行”主页上每个板的可用串行端口列表。
target:要搜索的字符串。允许的数据类型:char
terminal:搜索中的终端字符串。允许的数据类型:char
返回值
资料类型:bool

Serial.parseFloat()

描述
Serial.parseFloat()从串行缓冲区返回第一个有效的浮点数。parseFloat()以不是浮点数的第一个字符终止。如果超时,该函数将终止(请参见Serial.setTimeout())。
Serial.parseFloat()Stream实用程序类继承。
句法
*Serial*.parseFloat()
*Serial*.parseFloat(lookahead)
*Serial*.parseFloat(lookahead, ignore)
参量
*Serial*:串行端口对象。请参阅“ 串行”主页上每个板的可用串行端口列表。
lookahead:用于在流中向前查询浮点数的模式。允许的数据类型:LookaheadMode。允许lookahead值:

  • SKIP_ALL:扫描流中的浮点数时,除负号,小数点或数字以外的所有字符都将被忽略。这是默认模式。
  • SKIP_NONE:任何内容都不会被跳过,除非第一个等待的字符有效,否则流不会被触摸。
  • SKIP_WHITESPACE:仅跳过制表符,空格,换行符和回车符。
    ignore:用于跳过搜索中指示的字符。例如,用于跳过数千个分频器。允许的数据类型:char
    返回值:
    类型:float

Serial.parseInt()

描述
在输入的序列中查找下一个有效整数。如果超时,该函数将终止(请参见Serial.setTimeout())。
Serial.parseInt()Stream实用程序类继承。
尤其是:

  • 如果没有读取到可配置的超时值的字符,或者读取了非数字,则分析停止。
  • 如果在发生超时(请参见Serial.setTimeout())时未读取到有效数字,则返回0;否则返回0。

句法
*Serial*.parseInt()
*Serial*.parseInt(lookahead)
*Serial*.parseInt(lookahead, ignore)
参量
*Serial*:串行端口对象。请参阅“ 串行”主页上每个板的可用串行端口列表。
lookahead:用于在流中向前搜索整数的模式。允许的数据类型:LookaheadMode。允许lookahead值:

  • SKIP_ALL:扫描流中的整数时,将忽略数字或减号以外的所有字符。这是默认模式。
  • SKIP_NONE:任何内容都不会被跳过,除非第一个等待的字符有效,否则流不会被触摸。
  • SKIP_WHITESPACE:仅跳过制表符,空格,换行符和回车符。

ignore:用于跳过搜索中指示的字符。例如,用于跳过数千个分频器。允许的数据类型:char
返回值
下一个有效整数。资料类型:long

8.判断串口是否就绪** Serial

描述

指示指定的串行端口是否已就绪。

在具有本地USB的板上if (Serial)(或if(SerialUSB)在Due上)指示USB CDC串行连接是否打开。对于所有其他板卡和非USB CDC端口,这将始终返回true。

这是在Arduino IDE 1.0.1中引入的。

句法

if (Serial) while (!Serial) 等等

参量

没有

返回值

如果指定的串行端口可用,则返回true。如果在准备就绪之前查询Leonardo的USB CDC串行连接,则仅返回false。类型:bool

9、设置串口超时**

Serial.setTimeout()

描述
Serial.setTimeout()设置等待串行数据的最大毫秒数。默认值为1000毫秒。
Serial.setTimeout()Stream实用程序类继承。
句法
*Serial*.setTimeout(time)
参量
*Serial*:串行端口对象。请参阅“ 串行”主页上每个板的可用串行端口列表。
time:超时时间(以毫秒为单位)。允许的数据类型:long
退货
没有

注意和警告
使用通过*Serial*.setTimeout()以下方式设置的超时值的串行函数:

  • *Serial*.find()
  • *Serial*.findUntil()
  • *Serial*.parseInt()
  • *Serial*.parseFloat()
  • *Serial*.readBytes()
  • *Serial*.readBytesUntil()
  • *Serial*.readString()
  • *Serial*.readStringUntil()

也可以看看

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB
  }
}

void loop() {
  //proceed normally
}
/*
   Serial
   串口通讯实验
*/
int incomedate = 0;
void setup() {

  Serial.begin(9600); //设置串口波特率9600

  Serial.println(78, BIN);// "1001110"
  Serial.println(78, OCT);// "116"
  Serial.println(78, DEC);// "78"
  Serial.println(78, HEX);// "4E"
  Serial.println(1.23456, 0);// "1"
  Serial.println(1.23456, 2);// "1.23"
  Serial.println(1.23456, 4);// "1.2346"
  Serial.println('N');// "N"
  Serial.println("Hello world.");// "Hello world."
}
void loop() {

  if (Serial.available() > 0)//串口接收到数据
  {
    incomedate = Serial.read();//获取串口接收到的数据
    if (incomedate == 'H')
    {
      Serial.println("Good Job!");
    }
  }
  delay(1000);
}

使用的串口Serial其实是HardwareSerial类的实例化,实例化过程中传入了串口号,实现过程如下:

三. Serial1 和 Serial2 的使用

ESP32有三个串口,串口0,串口1,串口2.

名称 标号 引脚 可用否
串口0 Serial TX 是 GPIO1, RX 是 GPIO 3 可用,很多开发板默认用此连接USB/串口换芯片
串口1 Serial1 TX 是 GPIO10, RX 是 GPIO 9 不可用,默认被Flash占用
串口2 Serial2 TX 是 GPIO17, RX 是 GPIO 16 可用

arduino-esp32 中定义了 Serial,Serial1,Serial2 三个对象, Serial1 是无法拿来直接使用, Serial 和 Serial2 可以正常使用,所有除了Serial之外, Serial2 直接使用即可, 引脚接17 ,16
下面, 我们将上面的例子改为Serial2发送, Serial接受并打印

#include <Arduino.h>
String rev;
void setup() {
  Serial.begin(115200);
  Serial2.begin(115200);
}

void loop() {
  if(Serial2.available())
  {
    rev=Serial2.readString();
    Serial.println("This is ESP32 (serial0)");
    Serial.print("rev from serial2: ");
    Serial.println(rev);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342