# 首页

这是Skyone的原创教程，介绍了Arduino核心库的函数。

这个文档主要包含：Arduino核心库常用函数的用法以及示例。

主要包含以下分类：

* 必备函数
* 引脚控制
* 时间相关
* 串口通信

未完待续···

{% hint style="info" %}
本文档由作者纯手打，请勿转载，但可以部分引用，请注明出处。

出处可以是 <https://arduino.doc.skyone.host/> 或 <https://www.skyone.host/2021/02/27/arduino-he-xin-ku-chang-yong-han-shu/>
{% endhint %}

{% hint style="info" %}
<https://www.skyone.host> 就是我的博客，不要再说这个文档和里面的一篇文章有点相似了。
{% endhint %}


# 必备函数

想要写一个Arduino程序，这些函数是必须的。

## `setup()`

### 函数原型

```cpp
void setup();
```

### 作用

Arduino通电或复位后，即会开始执行 `setup()` 函数中的程序，该函数**只会执行一次**。

## `loop()`

### 函数原型

```cpp
void loop();
```

### 作用

在setup() 函数中的程序执行完后，Arduino会接着会**无限循环** `loop()` 函数。

## `main()` 函数呢？

我们知道，不管是C语言还是C++，程序的入口都是 `main()` 函数，那么为什么 Arduino 程序没有 `main()` 函数呢？

实际上，`main()` 函数并没有消失，它依然是 Arduino 程序的入口，只是 Arduino 设计人员将它移到了核心库里，让我们翻看一下 Arduino 核心库的 `main.cpp` 文件，它在 `Arduino安装目录/hardware/arduino/avr/cores/arduino` 里

```cpp
// main.cpp

#include <Arduino.h>

int atexit(void (* /*func*/ )()) { return 0; }

void initVariant() __attribute__((weak));
void initVariant() { }

void setupUSB() __attribute__((weak));
void setupUSB() { }

int main(void)
{
    init();

    initVariant();

#if defined(USBCON)
    USBDevice.attach();
#endif

    setup();

    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
    }

    return 0;
}
```

{% hint style="info" %}
一个程序不能有两个 `main()` 函数，Arduino 核心库已经定义了 `main()` 函数，因此我们的程序里不能包含 `main()` 函数。
{% endhint %}

## 示例

这个例子可以使 LED 闪烁

将 LED 的正极接电阻，再接 3 号引脚，负极接 GND 引脚

```cpp
/*
 * Author:  Skyone
 * Date:    2020/02/28
 * Website: https://www.skyone.host
 */

#define LED 3

void setup() {
    // Put your setup code here, to run once.
    // 将你用于初始化的代码放在这里，这些代码只会执行一次。

    pinMode(LED, OUTPUT);   // 将 `LED` 引脚初始化为输出模式
}

void loop() {
    // Put your main code here, to run repeatedly.
    // 将你的业务函数放在这里，这些代码会一直循环的运行。

    digitalWrite(LED, HIGH);    // 将 `LED` 引脚设为 高电平
    sleep(1000);                // 停止 1 秒
    digitalWrite(LED, LOW);     // 将 `LED` 引脚设为 低电平
    sleep(1000);                // 停止 1 秒
}
```


# 引脚控制

实现引脚控制是单片机程序的最简单最基本的功能

## 简介

实现引脚控制的第一步是定义引脚，这一步使用 `pinMode()` 函数，这一步决定了该引脚是输入还是输出。

然后我们使用例如 `digitalWrite()` 的函数控制引脚的具体电位。

## 目录

{% content-ref url="/pages/-MUcu6O4ujYjsjwguIsL" %}
[pinMode](/core/io/pinmode)
{% endcontent-ref %}

{% content-ref url="/pages/-MUd-vhFmQuJGtQCF6fI" %}
[digitalWrite](/core/io/digitalwrite)
{% endcontent-ref %}

{% content-ref url="/pages/-MUd2YTmWaeDV\_Y2cpCB" %}
[digitalRead](/core/io/digitalread)
{% endcontent-ref %}

{% content-ref url="/pages/-MUd4AXcLzwo950GJ1U9" %}
[analogWrite](/core/io/analogwrite)
{% endcontent-ref %}

{% content-ref url="/pages/-MUd96w1G-MajWhcxWE2" %}
[analogRead](/core/io/analogread)
{% endcontent-ref %}


# pinMode

定义引脚模式

## 函数原型

```cpp
void pinMode(uint8_t pin, uint8_t mode);
```

## 作用

在使用引脚前，需要先使用 `pinMode()` 定义引脚的模式。

## 参数

* `pin`

  指定引脚的编号，如： `10` , `A0` 等。
* `mode`

  指定引脚的模式。

其中，`mode` 可以是以下值：

| 模式             | 名称     | 介绍                                                           |
| -------------- | ------ | ------------------------------------------------------------ |
| `INPUT`        | 输入模式   | 可以读取该引脚的电位                                                   |
| `OUTPUT`       | 输出模式   | 可以控制该引脚的输出                                                   |
| `INPUT_PULLUP` | 输入上拉模式 | 启动板载电阻，使该引脚可以直接连接 5V 电压而不需要在之间另加电阻，但**不能接负电位，不能接大于 5V 的电压。** |

## 示例

```cpp
pinMode(3, INPUT);          // 设置 3 号引脚为 输入模式
pinMode(4, OUTPUT);         // 设置 4 号引脚为 输出模式
pinMode(5, INPUT_PULLUP);   // 设置 5 号引脚为 输入上拉模式
```


# digitalWrite

控制引脚的输出

## 函数原型

```cpp
void digitalWrite(uint8_t pin, uint8_t val);
```

## 作用

设置指定引脚的电位

{% hint style="info" %}
在调用此函数之前要保证该引脚已经 `pinMode` 过，且为输出模式。
{% endhint %}

## 参数

* `pin`

  指定引脚的编号，如： `10` , `A0` 等。
* `val`

  引脚的电平。

其中，`val` 可以是以下值：

| 值      | 名称  | 介绍        |
| ------ | --- | --------- |
| `HIGH` | 高电平 | 使引脚输出5V电压 |
| `LOW`  | 低电平 | 使引脚变为0V   |

## 示例

```cpp
digitalWrite(3, HIGH);  // 设置 3 号引脚为 高电平
digitalWrite(3, HIGH);  // 设置 3 号引脚为 低电平
```


# digitalRead

读取引脚的电位

## 函数原型

```cpp
int digitalRead(uint8_t pin);
```

## 作用

读取指定引脚的电位

{% hint style="info" %}
在调用此函数之前要保证该引脚已经 `pinMode` 过，且为输入模式。
{% endhint %}

## 参数

* `pin`

  指定引脚的编号，如： `10` , `A0` 等。

## 返回值

* `int` 整型

  指定的引脚的电位

返回值有以下两种可能：

| 值      | 名称  | 介绍       |
| ------ | --- | -------- |
| `HIGH` | 高电平 | 引脚电压为 5V |
| `LOW`  | 低电平 | 引脚电压为 0V |

## 示例

```cpp
// 假设 4 号引脚连接的是 高电平
int state = digitalWrite(4);
// state = HIGH; 也就是 stats = 1;
```


# analogWrite

控制引脚的输出

## 函数原型

```cpp
void analogWrite(uint8_t pin, int val);
```

## 作用

设置指定引脚的电压，而且**只有支持模拟输出的引脚才能使用此函数。**

判断是否支持模拟输出的标志是引脚编号旁有一个小波浪线，但 Arduino Uno 中 A 开头的引脚除外，它们既支持模拟输出，又支持模拟输入。

{% hint style="info" %}
在调用此函数之前要保证该引脚已经 `pinMode` 过，为输出模式，且只有支持模拟输出的引脚才能使用此函数。
{% endhint %}

## 参数

* `pin`

  指定引脚的编号，如： `10` , `A0` 等。
* `val`

  引脚的电平。

其中，`val` 的值的范围是 **0\~255**，也就是电压位 0 对应 0， 电压为 5V 对应 255，成比例对应

## 示例

```cpp
digitalWrite(3, 0);   // 设置 3 号引脚为 0V
digitalWrite(3, 255); // 设置 3 号引脚为 5V
digitalWrite(3, 233); // 设置 3 号引脚为 (5 * 233 / 255)V
```


# analogRead

读取引脚的电压

## 函数原型

```cpp
int analogRead(uint8_t pin);
```

## 作用

读取指定引脚的电位，**只有支持模拟输入的引脚才能调用此函数。**

**注意：** 在调用此函数之前要保证该引脚已经 `pinMode` 过，且为输入模式。

## 参数

* `pin`

  指定引脚的编号，如： `10` , `A0` 等。

## 返回值

* `int` 整型

  指定的引脚的电位

返回值的范围为 **0\~1023**

## 示例

```cpp
// 假设 4 号引脚连接的是 3V
int state = digitalWrite(4);
// state = 3 * 1023 / 5;
```


# 时间相关

控制时间也是非常重要滴

## 简介

控制时间分为两类：

* 延时
* 获取时间

## 目录

{% content-ref url="/pages/-MUdEvpI6QLGns5-Ie80" %}
[delay](/core/time/delay)
{% endcontent-ref %}

{% content-ref url="/pages/-MUdGk\_19XAr4kVc5Kxs" %}
[delayMicroseconds](/core/time/delaymicroseconds)
{% endcontent-ref %}

{% content-ref url="/pages/-MUdJjNnn1-h5Y8SqKtQ" %}
[millis](/core/time/millis)
{% endcontent-ref %}

{% content-ref url="/pages/-MUdKRMBpvgetSLkg9No" %}
[micros](/core/time/micros)
{% endcontent-ref %}


# delay

延时一定毫秒

## 函数原型

```cpp
void delay(unsigned long ms);
```

## 作用

是程序停止执行一段时间，就像 Windows 下 C 中的 `sleep()`

## 参数

* `ms`

  如你所加，程序暂停的时间，单位**毫秒**

## 示例

```cpp
delay(1000);    // 延时 1 秒
delay(333);     // 延时 333 毫秒
```


# delayMicroseconds

延时一定微秒

## 函数原型

```cpp
void delayMicroseconds(unsigned int us);
```

## 作用

是程序停止执行一段时间，就像 Linux 下 C 中的 `usleep()`

## 参数

* `us`

  如你所加，程序暂停的时间，单位**微秒**

## 示例

```cpp
delayMicroseconds(1314);    // 延时 1314 微秒
delayMicroseconds(521);     // 延时 521 微秒
```


# millis

获取 "Arduino时间"，而不是 UNIX时间

## 函数原型

```cpp
unsigned long millis(void);
```

## 作用

获取 Arduino 通电后或复位后到现在的时间，单位毫秒

就类似于 Python 里的 `time.time()` ，只不过单片机不知道现在的时间，所以不是 1970 年 1 月 1 日 0 时 0 分 0 秒起至现在的总秒数，而是Arduino 通电后或复位后到现在的时间。

## 返回值

* `unsigned long` 无符号整型

  Arduino 通电后或复位后到现在的时间，单位**毫秒**

## 示例

```cpp
void setup() {
    delay(500);

    int time = millis();
    // time 约等于 500
}

coid loop() {
    return;
}
```


# micros

获取 "Arduino时间"，而不是 UNIX时间

## 函数原型

```cpp
unsigned long micros(void);
```

## 作用

获取 Arduino 通电后或复位后到现在的时间，单位微秒

## 返回值

* `unsigned long` 无符号整型

  Arduino 通电后或复位后到现在的时间，单位**微秒**

## 示例

```cpp
void setup() {
    delay(500);

    int time = micros();
    // time 约等于 500,000
}

coid loop() {
    return;
}
```


# 串口通信

串口通信是Arduino debug的最常用的手段

## 简介

串口通信指的是Arduino开发板通过USB线与电脑通信，经常用于显示Arduino程序的运行状态、输入实时的指令等。此外，Arduino程序无法调试，因此串口通信就显得非常重要。

串口通信主要使用 `Serial` 类。

## 目录

{% content-ref url="/pages/-MYz9BhyTZUZJD-S2Rmn" %}
[begin](/core/serial/begin)
{% endcontent-ref %}

{% content-ref url="/pages/-MYz9O7zlbq0\_Kvvu\_Bj" %}
[print](/core/serial/print)
{% endcontent-ref %}

{% content-ref url="/pages/-MYz9oAHCfIksSXpSYkF" %}
[read](/core/serial/read)
{% endcontent-ref %}

{% content-ref url="/pages/-MYzA4r-Q\_VyYosk-9cv" %}
[其它方法](/core/serial/other)
{% endcontent-ref %}


# begin

使用串口之前，必须先初始化串口

{% tabs %}
{% tab title="常见用法" %}

## 函数原型

```cpp
void Serial.begin(long speed);
```

## 作用

初始化串口，指定串口数据传播速率(波特率)

## 参数

* `pin`

  指定引脚的编号，如： `10` , `A0` 等。

## 示例

```cpp
Serial.begin(9600);             // 一般将波特率设为9600
```

{% endtab %}

{% tab title="高级用法" %}

## 函数原型

```cpp
void Serial.begin(long speed, uint8_t config);
```

## 参数

* `pin`

  指定引脚的编号，如： `10` , `A0` 等。
* `config`

  可选，很少用到，可能的值如下：

  |     config可选配置    | 数据位 | 校验位 | 停止位 |
  | :---------------: | :-: | :-: | :-: |
  |    SERIAL\_5N1    |  5  |  无  |  1  |
  |    SERIAL\_6N1    |  6  |  无  |  1  |
  |    SERIAL\_7N1    |  7  |  无  |  1  |
  | SERIAL\_8N1(默认配置) |  8  |  无  |  1  |
  |    SERIAL\_5N2    |  5  |  无  |  2  |
  |    SERIAL\_6N2    |  6  |  无  |  2  |
  |    SERIAL\_7N2    |  7  |  无  |  2  |
  |    SERIAL\_8N2    |  8  |  无  |  2  |
  |    SERIAL\_5E1    |  5  |  偶  |  1  |
  |    SERIAL\_6E1    |  6  |  偶  |  1  |
  |    SERIAL\_7E1    |  7  |  偶  |  1  |
  |    SERIAL\_8E1    |  8  |  偶  |  1  |
  |    SERIAL\_5E2    |  5  |  偶  |  2  |
  |    SERIAL\_6E2    |  6  |  偶  |  2  |
  |    SERIAL\_7E2    |  7  |  偶  |  2  |
  |    SERIAL\_8E2    |  8  |  偶  |  2  |
  |    SERIAL\_5O1    |  5  |  奇  |  1  |
  |    SERIAL\_6O1    |  6  |  奇  |  1  |
  |    SERIAL\_7O1    |  7  |  奇  |  1  |
  |    SERIAL\_8O1    |  8  |  奇  |  1  |
  |    SERIAL\_5O2    |  5  |  奇  |  2  |
  |    SERIAL\_6O2    |  6  |  奇  |  2  |
  |    SERIAL\_7O2    |  7  |  奇  |  2  |
  |    SERIAL\_8O2    |  8  |  奇  |  2  |

## 示例

```cpp

Serial.begin(9600, SERIAL_8E2); // 将串口波特率设为9600，数据位8，偶校验，停止位2。
```

{% endtab %}
{% endtabs %}


# print

向串口写入数据，使我们在电脑上能看到Arduino的运行状态

## 函数原型

```cpp
size_t print(const __FlashStringHelper *);
size_t print(const String &);
size_t print(const char[]);
size_t print(char);
size_t print(unsigned char, int = DEC);
size_t print(int, int = DEC);
size_t print(unsigned int, int = DEC);
size_t print(long, int = DEC);
size_t print(unsigned long, int = DEC);
size_t print(double, int = 2);
size_t print(const Printable&);

size_t println(const __FlashStringHelper *);
size_t println(const String &s);
size_t println(const char[]);
size_t println(char);
size_t println(unsigned char, int = DEC);
size_t println(int, int = DEC);
size_t println(unsigned int, int = DEC);
size_t println(long, int = DEC);
size_t println(unsigned long, int = DEC);
size_t println(double, int = 2);
size_t println(const Printable&);
size_t println(void);
```

## 作用

向串口打印数据

`Serial.println()` 比 `Serial.print()` 多一个换行。

使用前必须用 `Serial.begin()` 初始化 `Serial`。

## 参数

* 数据

  就像C语言的 `prinft()` 一样。当然，似乎更像Java的 `System.out.println()`

## 返回值

输出的字符数

## 示例

```cpp
string str = "Skyone!";
Serial.print("Hello,");
Serial.println(str);
// Hello,Skyone!
```


# read

从串口获取数据，这样我们就能通过串口实时向Arduino发送命令

## 函数原型

```cpp
int HardwareSerial::read();
```

## 作用

向串口打印数据

`Serial.println()` 比 `Serial.print()` 多一个换行。

使用前必须用 `Serial.begin()` 初始化 `Serial`。

## 返回值

返回下一个字符串，就像C语言的 `getc()` 一样。

## 示例

```cpp
char c = Serial.read();
```


# 其它方法

这是一些其他的常用的串口函数

## `Serial.end`

### 函数原型

```cpp
void HardwareSerial::end()
```

### 描述

结束串口通信。

无参数，无返回值。

## `Serial.find`

### 函数原型

```cpp
bool find(char *target);
bool find(uint8_t *target);
bool find(char *target, size_t length);
bool find(uint8_t *target, size_t length);
bool find(char target);
```

### 作用

从串口缓冲区读取数据，直到读取到指定的字符串。

### 参数

* target

  需要搜索的字符串或字符
* length

  最多搜索的长度

### 返回值

Boolean型

* True：找到
* False：没有找到

## `findUntil()`

从串口缓冲区读取数据，直到读取到指定的字符串或指定的停止符。

### 原型

```cpp
bool Serial.findUntil(target, terminal)
```

### 参数：

* target

  需要搜索的字符串或字符
* terminal

  停止符

### 返回值：

bool型数据，是否找到

## `flush()`

等待正在发送的数据发送完成。

### 语法：

```cpp
void Serial.flush()
```

## `parseFloat()`

从串口缓冲区返回第一个有效的float型数据。

### 语法：

```cpp
float Serial.parseFloat()
```

### 返回值：

float 型数据

## `parseInt()`

从串口流中查找第一个有效的整型数据。

### 语法：

```cpp
int Serial.parseInt()
```

### 返回值：

int 型数据

## `peek()`

返回 1 字节的数据，但不会从接受缓冲区删除该数据。

与 `read()` 的不同之处在于 `read()` 读取数据后，会从接受缓冲区删除该数据。

### 语法：

```cpp
int HardwareSerial::peek()
```

### 返回值：

进入接受缓冲区的第一个字节的数据；如果没有可读数据，则返回-1


