课程目录

【概述】0 什么是智能系统

【阶段一】1 PlatformIO环境配置和GPIO

【阶段一】2 PWM与电机控制

【阶段一】3 外部中断和定时器中断

【阶段一】4 串口与串口中断

【阶段一】5 I2C与MPU6050模块

【阶段一】6 基础小车设计

【阶段二】7 智能系统概述

1.1 配置PlatformIO环境

1.1.1 安装VSCode

① 前往VSCode官网:https://code.visualstudio.com/Download

点击红框内的按钮开始下载

② 打开安装包,如图,点击我同意此协议,点击下一步
③ 选择安装目录,点击下一步
④ 点击下一步
⑤ 这里建议勾选将通过Code打开操作添加到目录上下文菜单,然后点击下一步
⑥ 点击安装,等待安装完成
⑦ 安装完成!

1.1.2 简单配置VSCode

如图是VSCode主界面:

第一次安装VSCode,默认是英文界面,下面我们将安装中文语言包。

点击左边栏的扩展按钮(红框的位置)

在搜索栏中输入Chinese,找到简体中文语言包,选择安装:

安装完成后重启VSCode即可。

1.1.3 在线安装PlatformIO

① 点击左边栏的扩展按钮,搜索PlatformIO,点击安装
② 等待插件安装完成。插件安装完成后左侧会出现PlatformIO的图标
③ 插件安装完成后,会自动执行安装程序,右下角会出现安装提示,如图:

安装速度会比较慢,请耐心等待

④ 如果看见如下图的提示,则PlatformIO成功安装

注:如果遇到报错,请关闭VSCode并重新打开,这将重新尝试安装。同时也可以尝试更换网络环境(比如手机热点)

重启VSCode即可看见PlatformIO主界面

1.1.4 建立一个新工程

① 点击PlatformIO图标,然后点击Open
② 点击New Project
③ 填写工程相关的信息

Name可以随便起,只要没有错误提示就行

Broad请选择Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, NO PSRAM)

Framwork选择Arduino

然后点击Finish

④ 确保网络状况良好,等待一会即可
⑤ 工程创建完成,如图
⑥ 尝试编译项目

选择右上角对钩(或者是右箭头)的旁边的小箭头,然后点击build开始编译

⑦ 成功编译项目!

当VSCode下方的终端显示[SUCCESS]即代表编译完成

1.2使用ESP32点亮一个LED!(GPIO输出)

1.2.1 引脚是什么?

引脚,英文叫 Pin, 就是从芯片以及一些电子元件内部电路引出与外围电路的接线的接口。

观察ESP32和其他的模块,这下板子上伸出来的金属针脚就是我们所说的引脚啦

引脚是做什么用的呢?

GPIO(General Purpose Input/Output),意为通用输入输出。我们可以通过配置ESP32的GPIO引脚来与其他的设备进行交流。

观察ESP32开发板,其中G+数字的引脚均为我们可以操作的GPIO引脚

可以观察到,除了G+数字的引脚,还有一些其他的引脚

  • GND引脚:顾名思义,GND的意思是ground,也就是接地,我们默认它的电压为0v
  • 3V3引脚:相对于GND输出3.3v电压的引脚

1.2.2 GPIO的输出

输出,就是向外表达我们的意图。我们可以通过GPIO的输出功能控制外设,比如:

  • 各色的LED
  • 电机

1.2.3 拿出所需的器材(并接线)

  • ESP32开发板
  • 面包板
  • 红绿灯
  • 杜邦线
  • 跳线
  • Type-C数据线
  • 一台装好PlatformIO环境的电脑

1.2.4 使用PlatformIO新建工程

1.2.5 认识工程的main.cpp

ESP32的主程序文件是src文件夹下的main.cpp:

创建工程时默认的main.cpp如下:

我们把一些用不到的东西删除,只留下esp32工程的骨架:

#include <Arduino.h>

void setup() {

}

void loop() {

}
  • 可以发现,骨架中只包含一个头文件和两个函数。
    • 首先是第一行的头文件:我们暂时不必关心它的细节,它只负责引入需要的库文件。
    • 接下来是setup()函数:这是ESP32启动时执行的第一个函数,设备上电后会运行,并且只执行一次,通常用来进行初始化操作。
    • 然后是loop()函数:在执行完setup()函数后,ESP32会开始执行loop()函数。loop的意思是“循环”,因此ESP32会不断重复执行这个函数,直到断电。

1.2.6 开始写代码

① 初始化GPIO口

前面提到我们可以通过GPIO的输出功能控制外设,那么现在我们要做的第一步,就是将GPIO口配置为输出模式。

我们使用pinMode(引脚, 模式);来配置GPIO口的模式。

因此,我们先将代码改成这样:

#include <Arduino.h>

void setup() {
    pinMode(5,OUTPUT);  // 配置引脚模式
}

void loop() {

}

接下来我会对这行代码进行解释:

  • pinMode的意思是“引脚模式”,pin代表引脚,mode代表模式,因此这个函数用于配置GPIO引脚的模式。
  • pinMode()的第一个参数是ESP32上引脚的编号。例如,红绿灯的R引脚连接在G5引脚上,因此配置时我们填入数字5。如果要配置G6引脚,填入数字6即可。
  • pinMode()的第二个参数代表配置的模式。当前我们希望控制红绿灯的亮灭,属于“输出”行为,因此第二个参数填写OUTPUT。
    • 很容易联想到,输出的反义词是输入。如果我们要将GPIO引脚配置为输入模式,只需将OUTPUT改为INPUT。这个我们稍后会详细讲解。
② 配置引脚状态

现在我们已经初始化好了第5个GPIO引脚的模式为输出模式了,那么我们应该如何控制GPIO引脚进行输出呢?

和刚才一样,先来和我输入这行代码:

#include <Arduino.h>

void setup() {
    pinMode(5,OUTPUT);  // 配置引脚5为输出模式
}

void loop() {
    digitalWrite(5, HIGH);  // 配置引脚5为高电平
}

没错,控制GPIO引脚的方法就是digitalWrite(5, HIGH);

这里我们暂且先不对这个语句进行讲解,现在让我们依次点击Build、Upload,然后观察ESP32上的红灯,如果前面的步骤都能正确完成的话,此时你应该能看到红灯成功亮起。

1.3让红绿灯亮起的原理

接下来,我们解释一下代码 digitalWrite(5, HIGH); 究竟做了什么。

  • digitalWrite 表示向指定引脚写入值。
  • 第一个参数“5”表示第5号引脚。
  • 第二个参数“HIGH”表示将引脚配置为高电平。

因此,这行代码的作用是将ESP32的第5号GPIO引脚设为高电平。

什么是高电平?

高电平和低电平是数字电路中的两个基本状态,分别表示不同的电压值。高电平对应较高电压(如3.3V或5V),代表“1”;低电平则对应较低电压(如0V),代表“0”。这两个状态用于控制设备的逻辑运算和信号传输。

当引脚配置为高电平时,会输出3.3V的电压。例如,当红绿灯的R引脚连接到ESP32的G5引脚时,R引脚的对地电压为3.3V,而GND引脚接地,电压为0V。由于引脚间形成电势差,红灯因此被点亮。

1.4 使用按键控制红绿灯(GPIO输入)

1.4.1 GPIO的输入

输入,就是接收他人向我们表达的意图。

GPIO的输入有两种模式,需要我们进行配置:

  • 上拉输入模式
  • 下拉输入模式

这一部分涉及到模电的知识,我们不作过多的探究,现阶段我们只需了解它们会有什么作用即可。

  • 在上拉输入模式下,引脚默认为高电平
  • 在下拉输入模式下,引脚默认为低电平

如果不配置上拉还是下拉,我们将无法了解引脚在上电后的状态,因此必须根据实际状况先行配置。

1.4.2 按键

按键的构造非常简单,它有两个引脚。当按下按键时,两引脚接通;松开时,两引脚之间相当于断路。

1.4.3 拿出所需的器材(并接线)

  • ESP32开发板
  • 面包板
  • 红绿灯
  • 按键
  • 杜邦线
  • 跳线
  • Type-C数据线
  • 一台装好PlatformIO环境的电脑

接线时需要注意,如果用上拉模式,按键的另一个引脚需要接在GND上;如果用下拉模式,按键的另一个引脚需要接在3V3上。

我们按照上拉模式进行接线。

1.4.4 配置GPIO引脚为输入模式

#include <Arduino.h>

void setup() {
    pinMode(5,OUTPUT);  
    pinMode(4,INPUT_PULLUP);
}

void loop() {
    
}

和刚才一样,我们依然使用pinMode(引脚, 模式);来配置GPIO口的模式。

  • pinMode()的第一个参数是ESP32上引脚的编号。
  • pinMode()的第二个参数代表配置的模式。当前我们希望读取按键是否按下,属于按键的“输入”行为,因此第二个参数填写INPUT_PULLUP,这将把G4引脚设置为输入模式,并且默认为高电平。

1.4.5 读取引脚的状态

一般而言,引脚的状态有两种:高电平和低电平

读取引脚的状态我们使用digitalRead(引脚),它会返回(告诉我们)当前的引脚是高电平(HIGH)还是低电平(LOW)。

如果当前G4引脚是高电平状态,那么digitalRead(引脚)就等于HIGH

如果当前G4引脚是低电平状态,那么digitalRead(引脚)就等于LOW

现在我们的接线方式是上拉,那么当按键断开时,G4引脚默认为HIGH,当我们按下按键时,G4引脚会被拉到GND,也就是低电平的状态,因此,我们只需检测G4引脚是否被拉低为低电平,即可判断按键是否被按下。

loop()函数会在ESP32通电后不断地循环运行。因此,我们可以在这个函数中不断地检查G4引脚的状态,当发现digitalRead() 的结果为LOW时,就执行操作。

#include <Arduino.h>

void setup() {
    pinMode(5,OUTPUT);
    pinMode(4,INPUT_PULLUP);  // 配置为上拉输入模式
}

void loop() {
    if(digitalRead(4) == LOW)  // 检查G4引脚是否为低电平
    {
        // 执行操作的语句
    }
}

这里简单介绍一些C++中的一些基本概念:

if语句是C++中的条件语句,if后括号中的条件成立,大括号内的语句才会执行。

==是C++中的运算符,用于判断符号两边的式子是否相等,须与=区分开,=是赋值语句。

1.4.6 按键消抖

由于按键内部使用的是机械式弹簧片来进行通断的,所以在按下和松手的瞬间会伴随有一连串的抖动,如图:

因此,我们需要将按键前后10ms的抖动给软件消除掉。

我们使用延时函数delay(),括号里填写延时的时长,单位为ms。

在延时期间,ESP32的运行会受到阻塞,因此这段时间ESP32并不会再去读取G4引脚的值,从而避免了抖动带来的影响。

void loop() {
    if(digitalRead(4) == LOW)  // 检查G4引脚是否为低电平
    {
        delay(10);  // 延时消抖
    }
}

接着我们来讲一下按键松手的情况:

在这里我们希望在按下按键后ESP32不执行操作,而当我们松开按键后,ESP32再执行相关的操作。

我们使用一个while循环来判断是否松手。如果没松手,则让ESP32堵塞在这里,直到松手。

void loop() {
    if(digitalRead(4) == LOW)  // 检查G4引脚是否为低电平
    {
        delay(10);  // 延时消抖
        while (digitalRead(4) == LOW);  // 等待松手
    }
}

松手后,按键依然会有抖动,因此我们还需要进行一次消抖

void loop() {
    if(digitalRead(4) == LOW)  // 检查G4引脚是否为低电平
    {
        delay(10);  // 延时消抖
        while (digitalRead(4) == LOW);  // 等待松手
        delay(10);  // 延时消抖
        
        // 其他的操作
    }
}

这段代码可以当做是使用按键的固定模板,它所做的操作就是检测按键是否按下。

1.4.7 按键,然后亮灯!

我们将开始写亮灯部分的代码。

想让灯亮起来,我们前面已经说过了,使用digitalwrite()将灯的引脚置为高电平即可。

void loop() {
    if(digitalRead(4) == LOW)  // 检查G4引脚是否为低电平
    {
        delay(10);  // 延时消抖
        while (digitalRead(4) == LOW);  // 等待松手
        delay(10);  // 延时消抖
        
        digitalWrite(5, HIGH);
    }
}

现在我们来理一下这段代码的逻辑:

检测是否按下了按键->如果按下,则让灯亮起。

这里相信大家已经发现了一些问题:在按下一次按键后,灯将会一直是亮起的状态,没法做到之前演示的按一次按键切换灯的状态。

这里介绍一种方法:用一个状态变量来控制灯的亮灭。

假设有一个状态变量status,它表示灯的状态。当status为1时,灯亮;当status为0时,灯灭。

当我们检测到按键按下时,我们只需改变检查status的值,再做对应的操作就可以实现按键控制灯的亮灭了。

现在,就让我们设置一个status变量,并且在按键按下后改变这个变量的状态

int status = 0;  // 设置一个status变量

void loop() {
    if(digitalRead(4) == LOW)  // 检查G4引脚是否为低电平
    {
        delay(10);  // 延时消抖
        while (digitalRead(4) == LOW);  // 等待松手
        delay(10);  // 延时消抖
        
        status = !status;  // 取反操作,改变变量的值
    }
}

!是逻辑取反运算符,它会将非0的数取反为0,将0取反为1。比如。。。

现在我们成功地在按键按下后改变了状态status,接下来,我们要让ESP32根据不同的状态来让灯做出正确的动作。

很简单,我们只需要判断status的值即可,如果status不为0,则灯亮,G5引脚置为高电平;如果为0, 则灯灭,G5引脚置为低电平。

int status = 0;  // 设置一个status变量

void loop() {
    // 检测按键是否按下
    if(digitalRead(4) == LOW)  // 检查G4引脚是否为低电平
    {
        delay(10);  // 延时消抖
        while (digitalRead(4) == LOW);  // 等待松手
        delay(10);  // 延时消抖
        
        status = !status;  // 取反操作,改变变量的值
    }
    
    // 让ESP32根据状态控制灯的亮灭
    if(status==0)
    {
        //灯灭
        digitalWrite(5,LOW);
    }
    else
    {
        //灯亮
        digitalWrite(5,HIGH);
    } 
}

这样我们就完成了完整的按键点灯逻辑,点击Build、Upload烧录到ESP32试试吧!

1.5 总结

  • 首先,我们带着大家安装了ESP32的开发环境,并且创建了ESP32工程。
  • 其次,也是最重要的需要掌握的是:我们可以使用pinMode()来配置GPIO的输入或输出模式,使用digitalRead()来读取引脚的状态,使用digitalWrite()来控制引脚的高低电平。
  • 我们还讲了GPIO输入模式下的两种状态,一个是上拉状态,一个是下拉状态,他们有什么区别。
  • 我们讲了高电平和低电平的含义,以及让灯亮起的原理。
  • 除此之外,我们还带大家熟悉了C/C++语言中的一些基本语法的用法。

以下给出本次实验的最终代码:

#include <Arduino.h>  // 包含头文件

//配置引脚模式
void setup() {  // ESP32启动时执行的第一个函数,只执行一次
    //G5
    pinMode(5,OUTPUT);
    //G4  按键一端接在G4,另一端接在GND上
    pinMode(4,INPUT_PULLUP);
}

int status = 0;

void loop() {  // setup函数执行完成以后执行的函数,不断循环loop函数
    // 读取按键的状态
    // HIGH LOW
    if(digitalRead(4)==LOW)
    {
        delay(10);  // 延时消抖
        while(digitalRead(4)==LOW);  // 等待松手
        delay(10);  // 延时消抖

        status=!status;  // !取反 非0->0  0->非0 改变变量的值
    }
    // 让ESP32根据状态控制灯的亮灭
    if(status==0)
    {
        //灯灭
        digitalWrite(5,LOW);
    }
    else
    {
        //灯亮
        digitalWrite(5,HIGH);
    } 
}
Avatar photo

作者 Anysets

发表回复