PART 3

Arduino开发入门

第三篇
第6章 Arduino基础入门
电子信息系统认知与实践·杂志风电子书
第6章 Arduino基础入门
6.1 Arduino平台概述
6.1.1 Arduino平台概述🔗
6.1.1 什么是Arduino🔗
6.1.2 Arduino IDE🔗
6.1.3 Arduino开发板/控制器🔗
6.1.4 第一个Arduino程序🔗
6.2 Arduino语言基础
6.2.1 Arduino语言基础🔗
6.2.1 程序结构🔗
6.2.2 变量和常量🔗
6.2.3 运算符🔗
6.2.4 程序控制🔗
6.3 LED点灯实验
6.3.1 LED点灯实验🔗
6.3.1 数字IO介绍🔗
6.3.2 时间控制🔗
6.3.3 LED发光管🔗
6.3.4 Arduino 配置🔗
6.3.5 Arduino库🔗
6.3.6 打开你的第一个项目🔗
6.4 串口通信
6.4.1 串口通信🔗
6.4.1 并行通信和串行通信🔗
6.4.2 常见串行通信协议🔗
6.4.3 RS232通信🔗
6.4.4 Arduino串口操作🔗
6.4.5 串口监视器🔗
6.5 GPIO输入输出
6.5.1 GPIO输入输出🔗
6.5.1 数字IO🔗
6.5.2 渐变LED和PWM🔗
6.5.3 按键输入🔗
6.5.4 电机驱动🔗
第6章

Arduino基础入门

第6章 Arduino基础入门 > 6.1 Arduino平台概述
6.1.1

Arduino平台概述

参考来源
📄 https://blog.51cto.com/dpjcn1990/2978565?b=totalstatistic
第6章 Arduino基础入门 > 6.1 Arduino平台概述
6.1.1

什么是Arduino

参考:https://zh.wikipedia.org/wiki/Arduino

Arduino 是一个开源嵌入式硬件平台,用来供用户制作可交互式的嵌入式项目。此外 Arduino 作为一个开源硬件和开源软件的公司,同时兼有项目和用户社区。该公司负责设计和制造Arduino电路板及相关附件。这些产品按照GNU宽通用公共许可证(LGPL)或GNU通用公共许可证(GPL)[1]许可的开源硬件和软件分发的,Arduino 允许任何人制造 Arduino 板和软件分发。 Arduino 板可以以预装的形式商业销售,也可以作为DIY包购买。

Arduino抛开了传统嵌入式需要底层开发的复杂操作,在不了解硬件的内部结构和寄存器设置的情况下,也可以基于Arduino进行和硬件相关的开发,可快速完成创意项目。

Arduino Software IDE 使用与C语言和C++相仿的编程语言,并且提供了包含常见的输入/输出函数的 Wiring 软件库。

第6章 Arduino基础入门 > 6.1 Arduino平台概述
6.1.2

Arduino IDE

Arduino 计划也提供了 Arduino Software IDE,一套以 Java 编写的跨平台应用软件。Arduino Software IDE 源自于 Processing编程语言以及 Wiring 计划的集成开发环境。它是被设计于介绍程序编写给艺术家和不熟悉程序设计的人们,且包含了一个拥有语法高亮、括号匹配、自动缩进和一键编译并将可执行文件烧写入 Arduino 硬件中的编辑器。

第6章 Arduino基础入门 > 6.1 Arduino平台概述
6.1.3

Arduino开发板/控制器

根据使用的微控制器不同,Arduino有多种型号的开发板,但所有Arduino板都支持Arduino IDE编程。这些开发板的差异在于输入和输出的数量(可以在单个板上使用的传感器,LED和按钮的数量),速度,工作电压,外形尺寸等。

  • Arduino UNO

目前使用最广泛的Arduino控制器,具有Arduino的所有功能,是初学者最佳选择。

  • 1.   处理器: ATmega328
  • 2.   工作电压: 5V
  • 3.   数字IO脚: 14 (6路PWM输出)
  • 4.   模拟输入脚: 6
  • 5.   Flash: 32 KB (其中0.5 KB 用于 bootloader)
  • 6.   SRAM: 2 KB (ATmega328)
  • 7.   工作时钟 16 MHz
  • Arduino Mega

一个增强型的控制器,提供了更多的输入/输出接口,以及拥有更大的程序空间和内存,是完成较大型项目的较好选择。

  • 1.   控制器: ATmega2560
  • 2.   工作电压: 5V
  • 3.   数字I/0口: 54 (15路PWM输出)
  • 4.   模拟输入口: 16
  • 5.   Flash: 256 KB (其中8 KB 用于 bootloader)
  • 6.   SRAM: 8 KB (ATmega328)
  • 7.   工作时钟 16 MHz
  • ESP8266

可以说ESP8266是正宗Arduino板子的杀手。ESP8266 是一款由乐鑫 Espressif 公司制作的低成本的 Wi-Fi 芯片,支持Arduino开发。ESP8266售价非常低廉,在某宝上一块ESP8266开发板不到20块钱,且内置超低功耗 Tensilica L106 32 位 RISC 处理器,CPU 时钟速度最高可达 160 MHz,因此是非常适合初学者的一款开发平台。当然,ESP8266还可以下载AT固件,这时就可以为其它单片机提供WIFI连接功能;也可以抛弃Arduino进行底层开发。

市场是比较普遍的是NodeMCU系列的ESP8266开发板。本人购置的有安信可的NodeMCU-8266 V1.2,其资料可见http://en.ai-thinker.com/pro_view-67.html,和https://docs.ai-thinker.com/esp8266/boards/nodemcu

AI Thinker的ESP8266模块和开发板的资料:https://gitee.com/ai-thinker/ESP8266/tree/master/esp8266

NodeMCU-8266 V1.2手册:https://docs.ai-thinker.com/_media/esp8266/boards/nodemcu8266_v1.2_e8_a7_84_e6_a0_bc_e4_b9_a6.pdf

  • ESP32

ESP32是ESP8266的升级版,比Arduino运行速度更高、存储容量更大,但是价格稍贵一些,

  •   功耗方面:ESP32的工作电流5µA相比ESP8266的20µA功耗更低。
  •   时钟速度方面:ESP32的时钟速度是ESP8266的两倍。
  •   接口扩展方面:GPIO的引脚更多。
  •   价格方面:ESP32约是ESP8266的两倍。
参考来源
📄 https://www.w3cschool.cn/arduino/arduino_overview.html 📄 文献:https://www.w3cschool.cn/arduino/arduino_board_description.html 📄 https://www.espressif.com/zh-hans/products/socs/esp8266 📄 https://www.espressif.com/zh-hans/products/socs/esp32 📄 https://docs.ai-thinker.com/esp32/boards/nodemcu_32s
第6章 Arduino基础入门 > 6.1 Arduino平台概述
6.1.4

第一个Arduino程序

使用Arduino Software IDE编写的程序被称为“sketch”。一个典型的 Arduino C/C++ sketch 程序会包含两个函数,它们会在编译后合成为 main() 函数:

  • setup() :在程序执行开始时会执行一次,用于初始化设置。
  • loop() :直到Arduino硬件关闭前会重复执行函数放的代码。

当你在Arduino IDE上新增项目,默认典型的程序如下:

```C

//setup()函数中的代码只会被运行一次,通常用来做一些初始化工作;

void setup() {

}

//loop()中的代码会被无限次地重复运行,程序的主体部分会写在这里。

void loop() {

}

```

ESP8266 NodeMCU 引脚功能速查

引脚功能说明
D0 (GPIO16)数字IO可用于深度睡眠唤醒,不建议用作PWM
D1 (GPIO5)数字IO / PWM / I2C SCL常用I2C时钟线
D2 (GPIO4)数字IO / PWM / I2C SDA常用I2C数据线
D3 (GPIO0)数字IO启动时需为高电平,建议避免使用
D4 (GPIO2)数字IO / PWM板载LED,低电平点亮
D5 (GPIO14)数字IO / PWM / SPI SCKSPI时钟线
D6 (GPIO12)数字IO / PWM / SPI MISOSPI主入从出
D7 (GPIO13)数字IO / PWM / SPI MOSISPI主出从入
D8 (GPIO15)数字IO / SPI SS启动时需为低电平,上拉输出
SD2 (GPIO9)数字IO仅用于Flash连接,不建议使用
SD3 (GPIO10)数字IO仅用于Flash连接,不建议使用
A0模拟输入0~3.3V,10位ADC(0~1023)
VIN外部电源输入7~12V
5V5V输出由稳压器提供,可给外设供电
3.3V3.3V输出最大输出电流约300mA
GND接地
第6章 Arduino基础入门 > 6.2 Arduino语言基础
6.2.1

Arduino语言基础

Arduino可以让初学者不用理会单片机复杂寄存器配置,然后用Arduino提供的API直观控制Arduino, 使用起来非常简单。Arduino使用C/C++语言编写程序。

第6章 Arduino基础入门 > 6.2 Arduino语言基础
6.2.1

程序结构

Arduino程序的基本结构如下:

``` C

void setup() {

// put your setup code here, to run once(这里代码只运行一次):

}

void loop() {

// put your main code here, to run repeatedly(这里代码不断运行):

}

```

第6章 Arduino基础入门 > 6.2 Arduino语言基础
6.2.2

变量和常量

  • 常量

程序中,不需要改变的可以用常量来表示,常量可以是字符也可以是数字,常规的使用范例如下:

``` C

#define 常量名 常量值

```

  • 变量

程序中,可以变的值称为变量,Arduino的变量如下表所示:

类型存储空间占用(BYTE)取值范围

|byte| 1| 0~255

|int| 2 | -32768~32768

|unsigned int | 2 | 0 ~ 65535

|word| 2 | 0 ~ 65535

|long|4 | -2147483648 ~2147483647

|unsigned long|4 | 0~4294967295

|short|2 |-32768~32767

|boolean|1|取值为false和true

|char|1|-128~127

|unsigned char|1| 0~255

|float|4| -3.4028235E+38~3.4028235E+38

|double|4|-3.4028235E+38~3.4028235E+38

|string|-|根据具体情况确定

|String|-|根据具体情况确定

|array|-|根据具体情况确定

|void| 0|只是一个标识符,不占用存储空间

常规的使用范例如下:

``` C

byte bVar;

int iVar;

float fvar;

```

第6章 Arduino基础入门 > 6.2 Arduino语言基础
6.2.3

运算符

类型运算符说明

|算术运算符| =| 赋值

|算术运算符| +| 加

|算术运算符| -| 减

|算术运算符| *| 乘

|算术运算符| /| 除

|算术运算符| %| 取模

|比较运算符| ==| 等于

|比较运算符| !=| 不等于

|比较运算符| <| 小于

|比较运算符| <=| 小于等于

|比较运算符| >| 大于

|比较运算符| >=| 大于等于

|逻辑运算符| &&| 逻辑“与”运算

|逻辑运算符| ||| 逻辑“或”运算

|逻辑运算符| !| 逻辑“非”运算

|复合运算符| ++| 自加

|复合运算符| --| 自减

|复合运算符| +=| 复合加

|复合运算符| -+| 复合减

第6章 Arduino基础入门 > 6.2 Arduino语言基础
6.2.4

程序控制

选择结构

  • 1. 形式1

```C

if(表达式){

语句;

}

```

  • 2. 双分支

```C

if(表达式){

语句;

}else{

语句2;

}

```

  • 3. 多分支

```C

if(表达式1){

语句1;

}else if(表达式2){

语句2;

}else if(表达式3){

语句3;

}

```

  • 4. case多分支

```C

switch(){

case 常量表达式1:

语句1;

break;

case 常量表达式2:

语句2;

break;

case 常量表达式3:

语句3;

break;

......

default:

语句n;

break;

}

```

循环结构

  • 1. while

```C

while(表达式){

语句;

}

```

  • 2. do until形式

```C

do{

语句;

}while(表达式);

```

  • 3. for循环形式

```C

for(表达式1;表达式2;表达式3){

语句;

}

```

  • 4. break 终止当前选择结构或者循环结构
  • 5. continue 跳过本次循环中剩下的语句

核心函数速查卡片

数字 I/O

函数语法说明
设置引脚模式pinMode(pin, MODE)MODE: INPUT, OUTPUT, INPUT_PULLUP
写数字信号digitalWrite(pin, VALUE)VALUE: HIGH (高电平), LOW (低电平)
读数字信号int val = digitalRead(pin)返回 HIGH 或 LOW

模拟 I/O

函数语法说明
读模拟值int val = analogRead(pin)返回 0~1023(10位ADC,对应0~3.3V或0~5V)
写 PWM 值analogWrite(pin, value)value: 0~255(ESP8266默认0~1023),仅PWM引脚可用

时间函数

函数语法说明
延时(毫秒)delay(ms)阻塞式延时,参数范围0~4294967295
延时(微秒)delayMicroseconds(us)精确短延时,最大约16383μs
获取运行时间unsigned long t = millis()返回程序启动以来的毫秒数,约50天溢出归零
获取微秒数unsigned long t = micros()返回程序启动以来的微秒数,约70分钟溢出归零

串口通信

函数语法说明
启动串口Serial.begin(speed)speed: 常用9600、115200
打印(不换行)Serial.print(val)可输出数字、字符串、变量
打印(换行)Serial.println(val)输出后自动换行
读取一个字节int data = Serial.read()返回-1表示无数据
缓冲区可读字节数int n = Serial.available()返回缓冲区中待读取的字节数
清空发送缓冲Serial.flush()等待发送完毕
二进制写入Serial.write(val)发送原始字节数据
第6章 Arduino基础入门 > 6.3 LED点灯实验
6.3.1

LED点灯实验

最最传统的单片机入门内容

第6章 Arduino基础入门 > 6.3 LED点灯实验
6.3.1

数字IO介绍

数字IO可以输出和输入二进制0、1表示的数字信号。低电平是数字信号0,输出接近于0V;高电平是1,其输出接近于芯片的VCC。

Arduino上每一个带有数字编号的引脚都是数字引脚,包括写有"A"编号的模拟输入引脚。使用这些引脚可以完成输入/输出数字信号的功能。

  • 1. IO模式
  •   输出,可以输出高、低电平,是推挽输出。输出既可以向负载灌电流(输出H),也可以从负载抽取电流(输出L)。 推拉式输出级既提高电路的负载能力,又提高开关速度。
  •   输入,通常的输入是具有高阻抗特性的三态缓冲器,在读取的时候通过读操作由高阻状态变为读取状态,其电压由外部决定。在输入状态下,IO的电平状态是不确定的,完全由外部输入决定,如果在该引脚悬空的情况下,读取该端口的电平是不确定的。
  •   上拉输入,顾名思义,就是输入端和VCC之间接了一个上拉的电阻,在引脚悬空的情况下,该端口的电平是高电平1。同时,由于阻抗恒定,因此不会出现浮空输入的跳变沿。当然,由于上拉输入其实是判断是否非零,这样如果外部信号很弱,读取的还是高电平。
  • 2. pinMode(pin,mode)

方法:pinMode(pin,mode)

参数:pin为指定配置的引脚编号,参数mode为指定的配置模式。其模式包括:

模式名称模式说明

|INPUT| 输入模式| IO的电平状态是不确定的,完全由外部输入决定

|OUTPUT| 输出模式| 可以输出高、低电平,是推挽输出。

|INPUT_PULLUP| 输入上拉模式| 在引脚悬空的情况下,该端口的电平是高电平1。

  • 3. digitalWrite

方法:digitalWrite(pin,value)

参数:pin为指定输出的引脚编号。参数value为要指定的输出电平,使用HIGH指定输出高电平,使用LOW指定输出低电平。

注意:1:为Arduino工作电压(Vcc电压);2:为0V。

  • 4. digitalRead

方法:digitalRead(pin)

参数:pin为指定读取状态的引脚编号。

注意:当Arduino以5v供电时,会将范围为1.5v以下的作为Low识别,将范围大于3.5v的电压作为High识别。

过高或者过低的输入电压会损坏单片机。

  • 5. 示例

```C

#define LED_PIN 16

#define KEY_PIN 5

void setup() {

// put your setup code here, to run once(这里代码只运行一次):

pinMode(LED_PIN, OUTPUT);

pinMode(KEY_PIN, INPUT);

}

void loop() {

// put your main code here, to run repeatedly(这里代码不断运行):

digitalWrite(LED_PIN, digitalRead(KEY_PIN));

}

```

第6章 Arduino基础入门 > 6.3 LED点灯实验
6.3.2

时间控制

  • 1. delay()

毫秒级延时,它接受单个整数(或数字)参数,此数字表示时间(以毫秒为单位)。当程序遇到这个函数时,需要等到下一行代码。问题是,delay()函数并不是让程序等待的好方法,因为它被称为阻塞(blocking)函数。

```C

delay(ms); //ms 是以毫秒为单位暂停的时间(无符号长整型)

```

  • 2. delayMicroseconds()

微秒级延时,它接受单个整数(或数字)参数,此数字表示时间(以微秒为单位)。

```C

delayMicroseconds (us) ; //us 是要暂停的微秒数(无符号整型)

```

  • 3. millis()

此函数用于返回Arduino板开始运行当前程序时的毫秒数。这个数字在大约50天后溢出,即回到零。

  • 4. micros()

此函数返回自程序启动后的微秒数(无符号长整型)。

第6章 Arduino基础入门 > 6.3 LED点灯实验
6.3.3

LED发光管

LED是被称为"发光二极管"的半导体,是 "Light Emitting Diode" 的首字母简写。当该发光二极管施加正向电压时,电子和空穴就会移动并在结合部再次结合,正是再结合的能量转变成光而发出。

LED的种类繁多,依发光波长大致分为可见光与不可见光两类。不可见光led,通常波长850至1550奈米范围,短波长红外光可作为红外线无线通讯使用,如红外线led应用在影印纸张尺寸检知、家电用品遥控器、工厂自动检测、自动门、自动冲水装置控制等;长波长红外光,则应用在中、短距离光纤通讯上,作为光通讯用光源。可见光led产品有红、橙、黄、绿、蓝、紫等各种颜色,主要以显示用 途为主。以亮度1烛光 (cd) 作为一般亮度和高亮度之分界点。

常见的LED有直插和贴片式两种,下面时网上的商品介绍:

点亮LED需要施加正向的电压,且需给予一定的电流,常见的电压2V左右,电流是1-20mA(不同的电流亮度不同)。例如https://atta.szlcsc.com/upload/public/pdf/source/20160507/1462613613540.pdf所展示的一款红光LED,其Forward Voltage(电压)在20mA时的典型值为2V,此时亮度典型值为61mcd。

当然,通常我们进行实验时不需要这么高的亮度,因此NODEMCU-32说明书图所示,对于3.3V的IO输出,通常可以串联470欧姆电阻,这样可以供给发光二极管$(3.3V-2V)/470=2.5mA$。同理,使用5V的IO驱动时,通常是串联1K电阻,这样可以供给发光二极管$(5V-2V)/1K=3mA$的电流,能够让LED具有一定的亮度。

参考来源
📄 图片来自:https://www.rohm.com.cn/electronics-basics/led/led_what1
第6章 Arduino基础入门 > 6.3 LED点灯实验
6.3.4

Arduino 配置

下载

可以从Arduino官方网站的下载页面获得不同版本的Arduino IDE。网址:https://www.arduino.cc/en/Main/Software。

启动Arduino IDE

下载Arduino IDE软件后,需要解压缩该文件夹。在文件夹中,你可以找到带有arduino图标的应用程序图标。双击该图标以启动IDE。

启动后就可以进入度Arduino IDE

添加ESP8266支持

进入首选项(Preferences),找到附加开发板管理器地址(Additional Board Manager URLs),并在其后添加如下信息:

http://arduino.esp8266.com/stable/package_esp8266com_index.json

点击工具 - 开发板 - 开发板管理器,进入开发板管理器界面:

找到 esp8266并安装,安装完成后,重启 Arduino IDE 软件。在工具 - 开发板选项中即会看到 ESP8266 开发板的选项。

参考来源
📄 ESP32的地址为:https://dl.espressif.com/dl/package_esp32_index.json 📄 STM32系列:http://dan.drown.org/stm32duino/package_STM32duino_index.json 📄 Windows 10 IoT Core: https://github.com/ms-iot/iot-utilities/raw/master/IotCoreAppDeployment/ArduinoIde/package_iotcore_ide-1.6.6_index.json
第6章 Arduino基础入门 > 6.3 LED点灯实验
6.3.5

Arduino库

官方提供的库可以直接从Arduino IDE中导入。

除了可以直接使用Arduino提供的标准库之外,还可以使用第三方库。第三方库可以通过菜单中的“程序”->“导入库…”->“Add Library…”。

添加DHT11第三方库

引入库文件(流水灯不需要引入库文件,但如果使用DHT11等模块,通常需要引入相应的库文件)。

由于源代码中首行引入的 DHT 库并不是 Arduino IDE 内置的库文件,需要先点击项目 - 加载库 - 管理库进入库管理器,搜索安装如下两个依赖库(DHT sensor library):

常用其它库

  • SSD1306液晶显示,https://pan.baidu.com/s/1lp471Wr89ZZfDTCPePJ12g
  • Adafruit Unified Sensor
  • aREST 框架可以为一些常见的嵌入式开发板提供 RESTful 接口,支持通过串口、Wi-Fi、以太网、蓝牙等硬件发送命令至开发板,激发特定的操作,并将数据以 JSON 的格式返回给控制端用户。
  • 步进电机库,https://pan.baidu.com/s/10oueXb-jLIlDRt0yPjJg8Q
  • MPU6050库,https://pan.baidu.com/s/13D1JqfRUWlyUzESAZmVqFQ
  • ESP32Servo,ESP32舵机库,https://pan.baidu.com/s/1DdDCC9O47bMiV76TTnpMTA
第6章 Arduino基础入门 > 6.3 LED点灯实验
6.3.6

打开你的第一个项目

软件启动有几个选项:

*创建一个新项目:选择Flie→New

*打开一个现有的项目:选择File→Open

*打开一个现有的项目:选择File→Example→Basics→Blink

选择Arduino主板

转到Tools→Board,选择自己的Arduino板。Arduino IDE 支持非常多的基于 ESP8266 芯片设计的开发板,可以根据自己购买的开发板的具体型号,在编译前选择对应的开发板选项(工具 - 开发板),示例使用的是开源的 NodeMcu v1.1 开发板,编译代码前确保选择正确:

输入代码

  • 1. NODEMCU-AMICA代码:

``` C

void setup()

{

// initialize digital pin 16 as an output.

pinMode(16, OUTPUT); // GPIO16是User LED

}

// the loop function runs over and over again forever

void loop()

{

delay(10);

digitalWrite(16, HIGH); // turn the LED on (HIGH is the voltage level)

delay(1000); // wait for a second

digitalWrite(16, LOW); // turn the LED off by making the voltage LOW

delay(1000); // wait for a second

}

```

将LED连接至GPIO16,NODEMCU-AMICA的引进定义如下图所示,注意不同型号的ESP8266开发板的引脚布局不尽相同,需要查阅相关资料。

<font color="red" bold size="5"> 注意事项:

ESP8266 芯片有自己的引脚(GPIO)布局,不同型号的ESP8266开发板的引脚布局不尽相同。源代码中的 LED接到了16 ,指的并不是开发板的引脚 D16 ,而是 ESP8266 的引脚 GPIO 16 ,也就是D0。

  • 2. AI-THINKER NodeMcu-32 V1.2 代码:

``` C

void setup()

{

// initialize digital pin 16 as an output.

pinMode(16, OUTPUT); // GPIO16是User LED

}

// the loop function runs over and over again forever

void loop()

{

delay(10);

digitalWrite(16, HIGH); // turn the LED on (HIGH is the voltage level)

delay(1000); // wait for a second

digitalWrite(16, LOW); // turn the LED off by making the voltage LOW

delay(1000); // wait for a second

}

```

板子上有EN和ID0两个按键,下载时需根据提示同时按下EN和IO0,然后先松开EN,然后松开IO0。

选择串口

转到Tools→Serial Port菜单。通常是COM3或更高(COM1和COM2通常保留为硬件串行端口)。

下载程序

现在,只需点击环境中的“Upload”按钮。等待几秒钟,你将看到板上的RX和TX LED灯闪烁。如果上传成功,则状态栏中将显示“Done uploading”消息。

我手里的AI-THINKER的ESP32开发板,在下载时需要手动操作一下。

```

项目使用了 198842 字节,占用了 (15%) 程序存储空间。最大为 1310720 字节。

全局变量使用了13248字节,(4%)的动态内存,余留314432字节局部变量。最大为327680字节。

esptool.py v3.0-dev

Serial port COM3

Connecting........

```

当看到以上提示后,同时按下EN和IO0,然后先松开EN,然后松开IO0,即可以完成下载,提示如下:

```

项目使用了 198842 字节,占用了 (15%) 程序存储空间。最大为 1310720 字节。

全局变量使用了13248字节,(4%)的动态内存,余留314432字节局部变量。最大为327680字节。

esptool.py v3.0-dev

Serial port COM3

Connecting........_____.....__

Chip is ESP32-D0WD (revision 1)

Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None

Crystal is 40MHz

MAC: 94:3c:c6:11:76:c4

Uploading stub...

Running stub...

Stub running...

Configuring flash size...

Auto-detected Flash size: 4MB

Compressed 8192 bytes to 47...

Writing at 0x0000e000... (100 %)

Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 949.8 kbit/s)...

Hash of data verified.

Compressed 17120 bytes to 11164...

Writing at 0x00001000... (100 %)

Wrote 17120 bytes (11164 compressed) at 0x00001000 in 1.1 seconds (effective 129.5 kbit/s)...

Hash of data verified.

Compressed 198960 bytes to 102952...

Writing at 0x00010000... (14 %)

Writing at 0x00014000... (28 %)

Writing at 0x00018000... (42 %)

Writing at 0x0001c000... (57 %)

Writing at 0x00020000... (71 %)

Writing at 0x00024000... (85 %)

Writing at 0x00028000... (100 %)

Wrote 198960 bytes (102952 compressed) at 0x00010000 in 9.5 seconds (effective 166.7 kbit/s)...

Hash of data verified.

Compressed 3072 bytes to 128...

Writing at 0x00008000... (100 %)

Wrote 3072 bytes (128 compressed) at 0x00008000 in 0.1 seconds (effective 327.7 kbit/s)...

Hash of data verified.

Leaving...

Hard resetting via RTS pin...

```

第6章 Arduino基础入门 > 6.4 串口通信
6.4.1

串口通信

串口通信是单片机与其他设备之间最简单、最常见的通信方式。

第6章 Arduino基础入门 > 6.4 串口通信
6.4.1

并行通信和串行通信

  • 1. 并行通信: 将数据字节的各位分别用数据线同时进行传输是并行的通信方式,例如以8Bit一个字节为单位传输,需要8根数据线,还有时钟、读写等控制线。并行方式具有控制简单、传输速度快的特点。但由于传输线较多,在长距离传输时成本较高且接收时存在困难。
  • 1. 串行通信: 如果传输数据时各位都通过同一根数据线依次传输,一次只传一个位则是串行的通信方式。串行通信所需的传输线少,长距离传送时成本低,但数据的控制比并行通信复杂。我们常用的USB就是串行的通信方式。
  •   串行通信又分为同步和异步两种通信模式。在同步模式下,为了实现收发双方完全同步,通常是由发送方时钟对接收方时钟的直接控制或者接收方使用发送方时钟进行通信。而在异步模式下,收发双方各自使用各自的时钟控制通信过程。由于异步通信不要求收发时钟严格一致,因此实现容易,不需要额外的时钟线硬件连接,但通常需要在时间上预留帧间隔、起始同步等,传输效率低于同步模式。
  •   硬件成本:并行>同步>异步
  •   波特率:波特率是每秒传输码元的个数,在串行通信中是每秒传送的比特数,单位是:位/秒(bps)
第6章 Arduino基础入门 > 6.4 串口通信
6.4.2

常见串行通信协议

  • 1. SPI:

同步通信模式,不需要设置波特率,通常包括MOSI、MISO、CLK、CS等数据线;是单片机常见的外设连接方式,可以连接ADC、DAC等设备。

  • 2. I2C

同步通信模式,不需要设置波特率,通常有SDA、SCL两根数据线,也是单片机最常见的外设连接方式。常见的器件包括传感器、RTC、FLASH等。

  • 3. USART和RS-232

USART是通用异步串行通信方式,由于是异步方式,因此需要设置波特率,由于没有片选协议,因此这种通信方式通常只能点对点通信,不能1对多或者多对1.RS-232是一种USART的电平和接口标准。最简单的接口有TXD、RXD和GND三根线。收发机连接时,需要交叉连接,即发送方TXD接接收方RXD。标准RS-232电平标准一般为±12V,+12V为逻辑负(0),-12V为逻辑正(1)。由于采用电压方式通信,因此232的通信距离和速率有限,长距离通信时易收到干扰。在板内连接时直接连接两个USART通常我们也称为5V/3.3V 串口连接。

  • 3. RS-485

RS-485是工业总线,有两线制和四线制两种接线,四线制很少采用,多采用的是两线制接线方式。两线制需要485+、485-两根线,在同一总线上最多可以挂接32个节点。在低速、短距离、无干扰的场合可以采用普通的双绞线,反之,在高速、长线传输时,则必须采用阻抗匹配(一般为120Ω)的RS485专用电缆(STP-120Ω(用于RS485 & CAN)一对18AWG),而在干扰恶劣的环境下还应采用铠装型双绞屏蔽电缆(ASTP-120Ω(用于RS485 & CAN)一对18AWG)。RS-485接口的逻辑“1”以两线间的电压差为+(2-6)V表示;逻辑“0”以两线间的电压差为-(2-6)V表示。由于采用平衡驱动器和差分接收器的组合,485总线抗共模干能力,但是只能进行半双工通信,也就是收的时候不能发,发的时候不能收。

  • 3. RS-422

RS-422是一系列的规定采用4线,全双工,差分传输,多点通信的数据传输协议。硬件构成上EIA-422 (RS-422)分别标示为R+ R- T+ T- .缺点是布线成本高,容易搞错。你可以想象为一个全双工的EIA-422(RS-422)是由两组EIA-485(RS-485)分别担任收发构成。

第6章 Arduino基础入门 > 6.4 串口通信
6.4.3

RS232通信

串口参数

RS-232串口通信参数的主要参数有波特率、数据位、停止位和奇偶校验位:

  • 1. 波特率: 常见的数据传输速率为50、75、 100、150、300、600、1200、2400、4800、9600、19200,单位bps。
  • 2. 数据位: 标准的数据位可以有5、7和8位三种长度,通常我们可以用8bit。
  • 3. 停止位: 用于表示单个包的最后一位,典型的值为1,1.5和2位。停止位越长,允许双方的时钟差的门限越高,但包之间的间隔越长,也就是说传输效率越低。通常1个停止位就足够了。
  • 4. 奇偶校验位: 奇偶校验是一种简单的检错方式,包括无校验、奇校验和偶校验三种校验模式。在偶和奇校验时,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是00101011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位位1,这样就有5个逻辑高位。
  • 5. 参数表示: 通常的表示方法例如(9600,N,8,1),对应于9600波特率,无奇偶校验(N),8位长度,1停止位。

通信格式

  • 1. 电平

单片机的TX和RX引脚输出电平是TTL电平,幅值由单片机工作电压决定,要转换为RS-232标准时,通常使用MAX232或MAX3232将传输的数据由TTL电平转换成RS232电平,信号幅值约7V。

  • 2. 数据结构

数据长度设置为8时其数据结构:

  • 3. 波特率和时间

图中,TTL信号和RS232信号相反,即TTL为低电平(0V)时RS232表现为高电平(约为3-9V),TTL为高电平(3V)时RS232表现为低电平(-3到-9V)。图中发送数据为0xAA时,可以看到平常电平为H,起始位为1个时间长度的L,然后是0xAA依次由低至高依次发送(01010101)。如图示,我们测量出9600波特率对应的每一位对应的时长为1/9600=104(us)。这样对于(9600,N,8,1)一包十个比特(1位起始位,8位数据位和1位停止位)共占约1.05ms。

参考来源
📄 图片参考自:https://blog.csdn.net/u013072995/article/details/120017651 📄 图片参考自:http://www.elecfans.com/emb/jiekou/20171101573461.html 📄 图片参考自:https://blog.csdn.net/u013072995/article/details/120017651
第6章 Arduino基础入门 > 6.4 串口通信
6.4.4

Arduino串口操作

  • 1. 串口设置——Serial.begin()
  •   功能:开启串口,通常置于setup()函数中。
  •   使用:
  •   Serial.begin(speed);
  •   Serial.begin(speed,config);
  •   参数:
  •   speed: 波特率
  •   config:设置数据位、校验位和停止位。例如Serial.begin(speed,Serial_8N1); Serial_8N1中:8表示8个数据位,N表示没有校验,1表示有1个停止位。
  •   示例

```C

void setup() {

Serial.begin(9600); // open serial port, sets data rate to 9600 bps

}

```

  • 2. 串口停止——Serial.end()
  •   功能:停止串口,此时IO口可以作为数字IO使用。
  •   使用:
  •   Serial.end();
  •   参数:
  •   示例

```C

void loop() {

Serial.end(); // close serial port

}

```

  • 3. 清空串口缓存——Serial.flush()
  •   功能:1.0版本之前为清空串口缓存,现在该函数作用为等待输出数据传送完毕。
  •   使用:
  •   Serial.flush();
  •   参数:
  •   示例

```C

void loop() {

while(Serial.read()>= 0){}

}

```

  • 4. 输出串口数据——Serial.print()
  •   功能:写入字符串数据到串口。
  •   使用:
  •   Serial.print(val);
  •   Serial.print(val,format);
  •   参数:
  •   val: 打印的值,任意数据类型
  •   format: 输出的数据格式,包括整数类型和浮点型数据的小数点位数。
  •   示例

```C

Serial.print(12, BIN) // "00010010"

Serial.print(12, DEC) // "12"

Serial.print(12, HEX) // "0C"

Serial.print(1.23456, 0) // 小数点后保留0位,"1"

Serial.print(1.23456, 2) // "1.23"

Serial.print(1.23456, 4) // "1.2346"

Serial.print('N') // "N"

Serial.print("Hello world.") //"Hello world."

```

  • 5. 读取串口数据——Serial.read()
  •   功能:读取串口一个字符,读完后删除已读数据。
  •   使用:
  •   Serial.read();
  •   参数:
  •   返回:返回串口缓存中第一个可读字节,当没有可读数据时返回-1,整数类型。
  •   示例

```C

// 读入串口的数据后,返回对应的ASC码

int cRect;

void setup() {

Serial.begin(9600);

while(Serial.read()>= 0){} //清空串口缓存

}

void loop() {

cRect = Serial.read();//读串口第一个字节

if(cRect!= 0xFF) {

Serial.print("Serial.read: ");

Serial.println(cRect);

}

delay(100);

}

```

如果需要串口返回对应字符,则程序如下,大家可以对比一下区别!

```C

char cRect; // int 和 char

void setup() {

Serial.begin(9600);

while(Serial.read()>= 0){} //清空串口缓存

}

void loop() {

cRect = Serial.read();//读串口第一个字节

// 判断值 0xFF 和 -1

if(cRect!= 0xff) {

Serial.print("Serial.read: ");

Serial.println(cRect);

}

delay(100);

}

```

  • 6. 输出串口数据缓冲字节数目——Serial.available()
  •   功能:判断串口缓冲器的状态函数,用以判断数据是否送达串口。
  •   使用:
  •   Serial.available();
  •   参数:
  •   返回:返回缓冲区可读字节数目
  •   示例

```C

// 读入串口的数据后,返回对应的ASC码

int cRect;

int i;

int nDataNum;

void setup() {

Serial.begin(9600);

while(Serial.read()>= 0){}; //清空串口缓存

}

void loop() {

if (Serial.available()>0) {

delay(100); //等待数据传完,这里是9600波特率的一个完整的packet时间

nDataNum = Serial.available();

Serial.print("Serial.read: ");

for(i=nDataNum;i;i--){

cRect = Serial.read();

Serial.println(cRect);

}

}

while(Serial.read()>=0){}; //清空串口缓存

}

```

运行结果

  • 7. 输出串口数据——Serial.peek()
  •   功能:读取串口一个字符,读完后不删除已读数据。
  •   使用:
  •   Serial.peek();
  •   参数:
  •   返回:返回串口缓存中下一字节(字符)的数据,如果没有返回-1,整数int型
  •   示例
  • 8. 输出串口数据——Serial.readBytes()
  •   功能:从串口读取指定长度length的字符到缓存数组。
  •   使用:
  •   Serial.readBytes(buffer,length);
  •   参数:
  •   buffer: 缓存变量
  •   length:设定的读取长度
  •   返回:返回存入缓存的字符数,0表示没有有效数据。
  •   示例

```C

// 读入串口的数据后,显示接收的字符串

char cBuffer[18];

int nDataNum=0;

void setup() {

Serial.begin(9600);

while(Serial.read()>= 0){} //清空串口缓存

for(int i=0; i<18; i++){

cBuffer[i]='\0';

}

}

void loop() {

// read data from serial port

if(Serial.available()>0){

delay(100);

nDataNum = Serial.available();

nDataNum = Serial.readBytes(cBuffer,nDataNum);

Serial.print("Serial.readBytes:");

Serial.println(cBuffer);

}

while(Serial.read() >= 0){}

for(int i=0; i<18; i++){

cBuffer[i]='\0';

}

}

```

  • 9. 输出串口数据——Serial.readBytesUntil()
  •   功能:从串口读取指定长度length的字符到缓存数组,直到遇到终止字符。
  •   使用:
  •   Serial.readBytesUntil(character ,buffer,length);
  •   参数:
  •   character : 查找的终止字符 (char)
  •   buffer: 缓存变量
  •   length:设定的读取长度
  •   返回:返回存入缓存的字符数,0表示没有有效数据。
  •   示例

```C

// 读入串口的数据后,显示#前的接收的字符串或整个字符

char cBuffer[18];

int nDataNum=0;

void setup() {

Serial.begin(9600);

while(Serial.read()>= 0){} //清空串口缓存

for(int i=0; i<18; i++){

cBuffer[i]='\0';

}

}

void loop() {

// read data from serial port

if(Serial.available()>3){

delay(100);

// 读取串口,直到遇到结束字符'#'

nDataNum = Serial.readBytesUntil('#',cBuffer,Serial.available());

Serial.print("Serial.readBytes:");

Serial.println(cBuffer);

while(Serial.read() >= 0){}

for(int i=0; i<18; i++){

cBuffer[i]='\0';

}

}

}

```

  • 10. 输出串口数据——Serial.readString()
  •   功能:从串口缓存区读取全部数据到一个字符串型变量。
  •   使用:
  •   Serial.readString();
  •   参数:
  •   返回:返回从串口缓存区中读取的一个字符串。
  •   示例
  • 11. 输出串口数据——Serial.readStringUntil()
  •   功能:从串口缓存区读取字符到一个字符串型变量,直至读完或遇到某终止字符。
  •   使用:
  •   Serial.readStringUntil(terminator);
  •   参数:
  •   terminator:终止字符(char型)
  •   返回:返回从串口缓存区中读取的一个字符串。
  •   示例
  • 12. 输出串口数据——Serial.readString()
  •   功能:从串口缓存区读取全部数据到一个字符串型变量。
  •   使用:
  •   Serial.readString();
  •   参数:
  •   返回:返回从串口缓存区中读取的一个字符串。
  •   示例
  • 13. 输出串口数据——Serial.parseFloat()
  •   功能:读串口缓存区第一个有效的浮点型数据,当读到第一个非浮点数时函数结束。
  •   使用:
  •   Serial.parseFloat();
  •   参数:
  •   返回:返回串口缓存区第一个有效的浮点型数据,数字将被跳过。
  •   示例
  • 14. 输出串口数据——Serial.parseInt()
  •   功能:读串口缓存区第一个有效的整型数据。
  •   使用:
  •   Serial.parseInt();
  •   参数:
  •   返回:从串口接收数据流中读取第一个有效整数(包括负数)。如果超时且读不到有效整数时,返回0
  •   示例
  • 15. 输出串口2进制数据——Serial.write()
  •   功能:二进制数据数据到串口。
  •   使用:
  •   Serial.write(val);
  •   Serial.write(str);
  •   Serial.write(buf, len);
  •   参数:
  •   val: 字节
  •   str: 一串字节
  •   buf: 字节数组
  •   len: buf的长度
  •   示例
第6章 Arduino基础入门 > 6.4 串口通信
6.4.5

串口监视器

Arduino IDE 内置串口监视器,可用于监控串口通信状态,使用前需进行以下设置。

  • 1. 在Tool==>Serial Port 正确选择Arduino的串口编号;
  • 2. Tool==>Serial Monitor打开串口监视器;
  • 3. 在程序中设定的串口波特率应该和串口监视器的设置一致。如果你的串口初始化代码写成Serial.begin(9600);就要选择9600 baud的选项;
  • 4. 串口监视器如图所示,其中Send按钮可以将输入框内输入的数据送到Arduino的串口,下面的文本框显示的是从串口接收到的数据。

实验电路

利用NODEMCU-ESP32开发板,GPIO2接自定义LED。

代码

```C

// 开机后LED亮,串口输入OFF关闭LED,串口输入ON打开LED

#define LEDPIN 2

String sComData="";

int nDataNum=0;

void setup() {

Serial.begin(9600);

while(Serial.read()>= 0){} //清空串口缓存

pinMode(LEDPIN, OUTPUT); // GPIO16是User LED

digitalWrite(LEDPIN, HIGH); //打开

}

void loop() {

// read data from serial port

if(Serial.available()>0){

delay(500);

// 读取串口,直到遇到结束字符'#'

sComData = Serial.readString();

Serial.print("Serial.readString:");

Serial.println(sComData);

if (sComData == "ON")

digitalWrite(LEDPIN, HIGH); //打开

else if (sComData == "OFF")

digitalWrite(LEDPIN, LOW); //关闭

while(Serial.read() >= 0){}

}

}

```

运行结果

实验步骤

点击IDE右上角的串行监视器按钮。将弹出Serial Monitor窗口。使用此窗口,您不仅可以将数据从计算机发送到Arduino Uno板,还可以从板上接收数据并将其显示在屏幕上。

串口数据可视化

Arduino IDE 自带串口监视器和串口绘图器,绘图器效果如下.

其它可视化工具

下面的3个绘图工具均可在IDE上的工具栏选择【项目】-【加载库】-【管理库】,然后搜索安装得到。

  • 1. Plotter
  • 2. FlexiPlot
  • 3. PlotPlus,兼容SimPlot
第6章 Arduino基础入门 > 6.5 GPIO输入输出
6.5.1

GPIO输入输出

本节介绍Arduino的GPIO操作,包括数字输入/输出、PWM输出以及电机驱动。

第6章 Arduino基础入门 > 6.5 GPIO输入输出
6.5.1

数字IO

数字IO可以输出和输入二进制0、1表示的数字信号。低电平是数字信号0,输出接近于0V;高电平是1,其输出接近于芯片的VCC。Arduino上每一个带有数字编号的引脚都是数字引脚,包括写有"A"编号的模拟输入引脚。使用这些引脚可以完成输入/输出数字信号的功能。控制数字引脚的输入输出功能需要用到3个函数,分别是 pinMode()、digitalWrite()、digitalRead(),对应的传参跟功能在上节“经典LED点灯程序”中有提及,这里简单列出。

  •   pinMode(pin,mode) :pin为指定配置的引脚编号,参数mode为指定的配置模式。
模式名称模式说明

|INPUT| 输入模式| IO的电平状态是不确定的,完全由外部输入决定

|OUTPUT| 输出模式| 可以输出高、低电平,是推挽输出。高电平输出向负载输出电流,低电平时从负载接收电流。

|INPUT_PULLUP| 输入上拉模式| 判断是否非零,这样如果外部信号很弱,读取的还是高电平。

  •   digitalWrite(pin,value) :pin为指定输出的引脚编号,参数value为要指定的输出电平,使用HIGH指定输出高电平,使用LOW指定输出低电平。
  •   digitalRead(pin): pin为指定读取状态的引脚编号,返回1则为高电平,0为低电平。
第6章 Arduino基础入门 > 6.5 GPIO输入输出
6.5.2

渐变LED和PWM

PWM输出

  • 1. PWM是什么?

PWM(Pulse Width Modulation,脉冲宽度调制)是一种受控的方波信号。高电平在一个周期中所占的比例称为占空比(Duty Cycle)。对于幅度为A、占空比为R的方波信号,其平均电压为 A×R,改变占空比R即可改变输出信号的平均电压。从而实现用数字量(占空比)控制模拟量(平均电压)。

  • 2. PWM的作用
  • 通过简单的平滑电路(例如RC),就可以生成真正的模拟输出量;
  • 不通过平滑电路,利用开关亮直接控制灯光亮度,调节电机转速;
  • 控制舵机角度;
  • 输出不同频率的信号,或者直接驱动D类功放输出音频;
  • 3. 数字IO和PWM

除特殊引脚外,大部分数字IO均可复用为PWM输出引脚。ESP8266中D0不能用作PWM输出,D3建议避免使用。

  • 5. 输出PWM数据——analogWrite()
  •   功能:往指定引脚写入数据。
  •   使用:
  •   analogWrite(pin,val);
  •   参数:
  •   pin:启用软件PWM的GPIO引脚
  •   val:输出的高电平时长值,一般在0到PWMRANGE范围,默认PWMRANGE是1023
  •   返回:无
  • 6. 改变PWM范围——analogWriteRange()
  •   功能:该功能用于改变PWMRANGE数值。
  •   使用:
  •   analogWriteRange(new_range);
  •   参数:
  •   new_range:PWM最长时长值,也可以理解为PWM精度范围。
  •   返回:无
  • 7. 改变PWM频率——analogWriteFreq()
  •   功能:该功能用于改变PWMRANGE数值。
  •   使用:
  •   analogWriteFreq(new_frequency) ;
  •   参数:
  •   new_frequency:新PWM频率,默认是1kHZ。
  •   返回:无
  • 8. 示例

以下为ESP8266 LED呼吸灯示例,板载LED连接GPIO16。

```C

#define LEDIO 16 // GPIO2是User LED

void setup() {

pinMode(LEDIO,OUTPUT); //设置引脚模式

analogWrite(LEDIO,0);

}

void loop() {

int nLum;

for(nLum=0;nLum<1024;nLum++){ //亮度增加

analogWrite(LEDIO,nLum);

delay(2);

}

for(nLum=1023;nLum>=0;nLum--){ //亮度减少

analogWrite(LEDIO,nLum);

delay(2);

}

}

```

  • 2. ESP32示例

将上述代码移植到ESP32后会出现编译错误,因为ESP32的Arduino框架未实现 analogWrite() 函数。

错误提示:

```

C:\Users\e\Documents\Arduino\ESP32_StepLumLED\ESP32_StepLumLED.ino: In function 'void setup()':

ESP32_StepLumLED:4:21: error: 'analogWrite' was not declared in this scope

analogWrite(LEDIO,0);

```

ESP32可通过LEDC或SigmaDelta外设实现PWM功能:

引自:https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/AnalogOut/SigmaDelta/SigmaDelta.ino

```C

#define ledPin 2

int val;

void setup() {

// channel 0 50 Hz

sigmaDeltaSetup(0, 50);

// pin 18 channel 0

sigmaDeltaAttachPin(ledPin,0);

// channel 0 关闭

sigmaDeltaWrite(0, 0);

}

void loop() {

for(val=0;val<255;val+=2) {

sigmaDeltaWrite(0,val);

delay(50);

}

}

```

以下是使用LEDC外设实现呼吸灯的示例:

```C

// use first channel of 16 channels (started from zero)

#define LEDC_CHANNEL_0 0

// use 12 bit precission for LEDC timer

#define LEDC_TIMER_12_BIT 12

// use 5000 Hz as a LEDC base frequency

#define LEDC_BASE_FREQ 5000

// fade LED PIN (replace with LED_BUILTIN constant for built-in LED)

#define LED_PIN 5

int brightness = 0; // how bright the LED is

int fadeAmount = 5; // how many points to fade the LED by

// Arduino like analogWrite

// value has to be between 0 and valueMax

void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {

// calculate duty, 4095 from 2 ^ 12 - 1

uint32_t duty = (4095 / valueMax) * min(value, valueMax);

// write duty to LEDC

ledcWrite(channel, duty);

}

void setup() {

// Setup timer and attach timer to a led pin

ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT);

ledcAttachPin(LED_PIN, LEDC_CHANNEL_0);

}

void loop() {

// set the brightness on LEDC channel 0

ledcAnalogWrite(LEDC_CHANNEL_0, brightness);

// change the brightness for next time through the loop:

brightness = brightness + fadeAmount;

// reverse the direction of the fading at the ends of the fade:

if (brightness <= 0 || brightness >= 255) {

fadeAmount = -fadeAmount;

}

// wait for 30 milliseconds to see the dimming effect

delay(30);

}

```

第6章 Arduino基础入门 > 6.5 GPIO输入输出
6.5.3

按键输入

  • 1. 按键

电子产品常通过开关得到用户的输入。拨码开关(下图左1)常用于配置,用开关和闭合输入某种功能的打开和关闭等。按键开关通常不带锁,按下则开关导通,否则开关断开。

  • 2. 基本操作

在单片机中我们常用的就是按键开关。对于少量按键,可直接将按键一端接GND,另一端接GPIO(配置为上拉输入模式),未按下时读取高电平,按下时读取低电平。其他类型的按键也可采用类似的连接方式。

```C

//按键控制LED,利用NodeMCU32开发板

#define LEDIO 2

#define KEYIO 0

void setup() {

Serial.begin(9600);

pinMode(LEDIO,OUTPUT);

pinMode(KEYIO,INPUT_PULLUP); //设置按键管脚上拉输入模式

}

void loop() {

int nvalue;

nvalue=digitalRead(KEYIO);

Serial.println(nvalue);

if(nvalue==0) {

digitalWrite(LEDIO,HIGH); }

else {

digitalWrite(LEDIO,LOW); }

}

```

  • 3. 按键去抖

机械按键在按下和释放瞬间会产生抖动(毛刺),需要通过去抖处理避免误触发。最简单的去抖方法是软件延时(通常10~20ms)。

```C

//按键控制LED,利用NodeMCU32开发板

#define LEDIO 2

#define KEYIO 0

void setup() {

Serial.begin(9600);

pinMode(LEDIO,OUTPUT);

pinMode(KEYIO,INPUT_PULLUP); //设置按键管脚上拉输入模式

}

void loop() {

int nvalue;

nvalue=digitalRead(KEYIO);

Serial.println(nvalue);

delay(10);

if(nvalue==0) {

digitalWrite(LEDIO,HIGH); }

else {

digitalWrite(LEDIO,LOW); }

}

```

第6章 Arduino基础入门 > 6.5 GPIO输入输出
6.5.4

电机驱动

电机是电子作品连接物理世界的桥梁。本节介绍如何使用GPIO驱动三种常见电机。常规使用的电机可以分为三种,直流电机、舵机和步进电机。

直流电机

直流电机(DC Motor)是最常见的电机类型,通过改变驱动电流或者驱动电压可以改变直流电机的转速。直流电动机通常只有两个引线,一个正极和一个负极。如果将这两根引线直接连接到电池,电机将旋转。如果切换引线,电机将以相反的方向旋转。

  • 1. 转速控制

直流电机的调速通常不通过改变电压实现,而是使用PWM信号控制。,由于电机驱动电流较大,通常需要使用三极管或MOSFET进行驱动。

PWM的输出可以使用上一小节提到的几种方法实现:

```C

#define MOTORIO 1

void setup() {

pinMode(MOTORIO,OUTPUT); //设置引脚模式

analogWrite(MOTORIO,0);

}

void loop() {

int nDcMotor;

for(nDcMotor=0;nDcMotor<1024;nDcMotor+=32){ //转速增加

analogWrite(MOTORIO,nDcMotor);

delay(2);

}

}

```

  • 2. 正反转控制

如需控制正反转,常用H桥电路。典型的H桥电机驱动电路由4个开关管和一个电机组成。通过控制不同开关管对的导通,改变电流流过电机的方向,从而控制电机正反转。

⚠️ 注意:H桥中同侧两个开关管不能同时导通,否则会造成短路烧毁器件。实际电路中必须通过硬件或软件实现死区保护。

实际应用中,通常使用集成化的H桥驱动芯片,如常见的L293D、L298N等。只需连接电源、电机和控制信号即可方便地控制电机。

典型电路如下图所示,ENA/ENB接PWM信号控制转速,IN1~IN4控制方向。

IN1IN2ENA电机状态
XX0停止
011正方向
101反方向

舵机/伺服电机

舵机由舵盘、减速齿轮组、位置反馈电位器、直流电机和控制电路组成。当控制电路板收到控制信号后,控制电机转动,电机的转动通过齿轮组驱动舵盘,同时带动位置反馈电位计,电位器输出和角度相关的电压给控制电路板,控制板根据电机的速度和方向以及角度判断是否到达目标角度,到达目标角度后停止转动。

该角度由控制线上的高电平脉冲持续时间确定。控制信号周期通常为20ms,高电平的持续时间决定电机转动的位置。脉宽固定时角度保持不变,因此舵机是非常好的位置伺服驱动器,适用于需要角度精确控制并保持的场景,如小车转向。

舵机选购时要注意舵机的体积、扭力、舵机的反应速度和虚位等指标。舵机的输出角度范围通常为0°~210°.

舵机有三个端子:电源(VCC)、接地(GND)和信号(Signal)。接地线通常为黑色或棕色,建议通过专用驱动模块连接。

对于AVR、STM32F4等为核心的Arduino开发板,可以用Servo库控制舵机,示例代码如下:

```C

/* 控制舵机 */

#include <Servo.h>

Servo myservo; // create servo object to control a servo

int npos; // variable to read the value from the analog pin

void setup () {

peripheral_setup();

// TODO: put your setup code here, to run once:

myservo.attach(1); // attaches the servo on pin 9 to the servo object

npos = 0;

}

void loop() {

// peripheral_loop();

// TODO: put your main code here, to run repeatedly:

npos ++;

if (npos>180) npos=0;

myservo.write(npos); // sets the servo position according to the scaled value

delay(5);

}

```

ESP32不支持标准Servo库,可使用LEDC或SigmaDelta库实现舵机控制。

```C

//使用SigmaDelta驱动舵机(该方法未经验证)

#define servoPin 23 //SigmaDelta驱动引脚

#define servo2 18 //LEDC驱动引脚

int nAngle;

int sigmaDeltaServoCalc(int angle)

{

// Period 20ms:256

// High Time = 0.5ms +(角度/180)*2ms

// Value = 0.5/20*256 + (角度/180)*2/20*256

float val = 6.4 + angle * 0.142;

return (int)val;

}

void setup() {

Serial.begin(9600);

sigmaDeltaSetup(0,50);

sigmaDeltaAttachPin(servoPin,0);

ledcSetup(1, 50, 8);

ledcAttachPin(servo2, 1);

}

void loop() {

nAngle = 0;

sigmaDeltaWrite(0,sigmaDeltaServoCalc(nAngle));

ledcWrite(1, sigmaDeltaServoCalc(nAngle));

Serial.print("Angle:");

Serial.println(nAngle);

Serial.print("Control:");

Serial.println(sigmaDeltaServoCalc(nAngle));

delay(2000);

nAngle = 45;

sigmaDeltaWrite(0,sigmaDeltaServoCalc(nAngle));

ledcWrite(1, sigmaDeltaServoCalc(nAngle));

Serial.print("Angle:");

Serial.println(nAngle);

Serial.print("Control:");

Serial.println(sigmaDeltaServoCalc(nAngle));

delay(2000);

nAngle = 90;

sigmaDeltaWrite(0,sigmaDeltaServoCalc(nAngle));

ledcWrite(1, sigmaDeltaServoCalc(nAngle));

Serial.print("Angle:");

Serial.println(nAngle);

Serial.print("Control:");

Serial.println(sigmaDeltaServoCalc(nAngle));

delay(2000);

}

```

步进电机

步进电机是一种无刷同步电机,以固定的步进角旋转,不同于普通无刷直流电机的连续旋转。常见步进角有30°、15°、5°、2.5°、2°和1.8°,分别对应每转12、24、72、144、180和200步。

步进电机可以根据需要精确移动到指定位置并保持。缺点是需要持续供电以保持位置。

``` C

//步进电机控制:未验证

#include <Stepper.h>

const int stepsPerRevolution = 90;

// change this to fit the number of steps per revolution

// for your motor

// initialize the stepper library on pins 8 through 11:

Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);

void setup() {

// set the speed at 60 rpm:

myStepper.setSpeed(5);

// initialize the serial port:

Serial.begin(9600);

}

void loop() {

// step one revolution in one direction:

Serial.println("clockwise");

myStepper.step(stepsPerRevolution);

delay(500);

// step one revolution in the other direction:

Serial.println("counterclockwise");

myStepper.step(-stepsPerRevolution);

delay(500);

}

```