语言参考-Variables

本篇文章均翻译自官方文档,为博主自用。方便博主提高英语水平。

必须注意的是,翻译不可能完全正确,若有矛盾之处或不能理解之处,需要查看原英文资料,并在comment中告知。

Arduino编程语言可以分为三个主要部分:函数、值(变量和常量)以及结构。

变量

Arduino 数据类型和常量。

常量

Floating Point Constants

Floating Point Constants

Description

类似于整数常量,浮点数常量用于使代码更具可读性。浮点数常量在编译时会被替换为表达式计算的值。

Example Code

float n = 0.005;  // 0.005 是一个浮点数常量

Notes and Warnings

浮点数常量也可以用多种科学记数法表示。'E' 和 'e' 都被接受为有效的指数标志。

浮点数常量计算结果:也计算结果为:
10.010
2.34E52.34 * 10^5234000
67e-1267.0 * 10^-120.000000000067

See also

HIGH | LOW

HIGH | LOW

定义引脚电平:高电平与低电平

在对数字引脚进行读取或写入时,引脚只能取/被设置为两个可能的值:HIGH(高电平)和LOW(低电平)。这些与true(真)和false(假)以及10是相同的。

高电平

HIGH(高电平)的含义(相对于引脚而言)根据引脚是被设置为INPUT(输入)还是OUTPUT(输出)有所不同。当引脚被配置为使用pinMode()INPUT(输入),并且使用digitalRead()读取时,如果满足以下条件,Arduino(ATmega)将报告HIGH(高电平):

  • 引脚处的电压高于3.0V(5V板)
  • 引脚处的电压高于2.0V(3.3V板)

引脚也可以使用pinMode()配置为输入,随后通过digitalWrite()设置为高电平。这将启用内部20K上拉电阻,除非外部电路将其拉低,否则将拉高输入引脚至高电平读数。通过向pinMode()函数传递INPUT_PULLUP参数,也可以实现这一点,这在下面的“定义数字引脚模式:INPUT、INPUT_PULLUP 和 OUTPUT”部分中有更详细的解释。

当引脚被配置为输出,并且使用digitalWrite()设置为HIGH(高电平)时,引脚处于:

  • 5伏特(5V板)
  • 3.3伏特(3.3V板)

在此状态下,它可以输出电流,例如点亮一个通过串联电阻连接到地的LED灯。

低电平

低电平的含义也根据引脚是被设置为输入还是输出而有所不同。当引脚被配置为输入,并且使用digitalRead()读取时,如果满足以下条件,Arduino(ATmega)将报告LOW(低电平):

  • 引脚处的电压低于1.5V(5V板)
  • 引脚处的电压低于1.0V(大约)(3.3V板)

当引脚被配置为输出,并且使用digitalWrite()设置为LOW(低电平)时,引脚处于0伏特(5V板和3.3V板均是如此)。在此状态下,它可以吸收电流,例如点亮一个通过串联电阻连接到+5伏特(或+3.3伏特)的LED灯。

See also

INPUT | INPUT_PULLUP | OUTPUT

INPUT | INPUT_PULLUP | OUTPUT

定义数字引脚模式:输入(INPUT)、带上拉(INPUT_PULLUP)和输出(OUTPUT)

数字引脚可以作为INPUT(输入)、INPUT_PULLUP(带上拉输入)或OUTPUT(输出)使用。通过pinMode()更改引脚会改变引脚的电气行为。

INPUT(输入)

使用pinMode()配置为INPUT(输入)的Arduino(ATmega)引脚被认为处于高阻抗状态。配置为INPUT(输入)的引脚对它们正在采样的电路的要求极低,相当于在引脚前串联一个100兆欧姆的电阻。这使它们适用于读取传感器。

如果你将引脚配置为INPUT(输入),并且正在读取一个开关,当开关处于打开状态时,输入引脚将会“悬浮”,导致不可预测的结果。为了确保在开关打开时能够得到正确的读数,必须使用上拉或下拉电阻。这个电阻的目的是在开关打开时将引脚拉到已知状态。通常选择10K欧姆的电阻,因为它的值足够低,可以可靠地防止输入悬浮,同时又足够高,不会在开关关闭时吸引太多电流。有关更多信息,请参阅数字读取串行教程。

如果使用下拉电阻,当开关打开时输入引脚将为LOW(低电平),当开关关闭时为HIGH(高电平)。

如果使用上拉电阻,当开关打开时输入引脚将为HIGH(高电平),当开关关闭时为LOW(低电平)。

INPUT_PULLUP(带上拉输入)

Arduino上的ATmega微控制器内置了上拉电阻(内部连接到电源的电阻),你可以访问这些电阻。如果你更愿意使用这些而不是外部上拉电阻,你可以在pinMode()中使用INPUT_PULLUP(带上拉输入)参数。

有关这方面的使用示例,请参阅输入上拉串行教程。

配置为INPUT(输入)或INPUT_PULLUP(带上拉输入)的引脚如果连接到低于地线(负电压)或高于正电源轨(5V或3V)的电压,可能会受损或被摧毁。

OUTPUT(输出)

使用pinMode()配置为OUTPUT(输出)的引脚被认为处于低阻抗状态。这意味着它们可以向其他电路提供大量的电流。ATmega引脚可以向其他设备/电路提供(source)或吸收(sink)高达40毫安(mA)的电流。这使它们适用于供电LED,因为LED通常使用的电流少于40mA。大于40mA的负载(例如电机)将需要晶体管或其他接口电路。

如果将配置为输出的引脚连接到地线或正电源轨,可能会受损或被摧毁。

See also

Integer Constants

Integer Constants

描述

整型常量是直接在草图中使用的数字,比如123。默认情况下,这些数字被当作 int 类型,但是你可以通过U和L修饰符来改变这一点(见下文)。

通常,整型常量被当作基数为10(十进制)的整数,但是可以使用特殊的符号(格式化器)输入其他进制的数字。

进制表格

进制示例格式化器注释
10(十进制)123
2(二进制)0b1111011前导 "0b"有效字符为0&1
8(八进制)0173前导 "0"有效字符为0-7
16(十六进制)0x7B前导 "0x"有效字符为0-9, A-F, a-f

十进制(基数10)

这是你熟悉的常识性数学。没有其他前缀的常量被假定为十进制格式。

示例代码:

n = 101;  // 等同于十进制的101 ((1 * 10^2) + (0 * 10^1) + 1)

二进制(基数2)

只有字符0和1是有效的。

示例代码:

n = 0b101; // 等同于十进制的5 ((1 * 2^2) + (0 * 2^1) + 1)

八进制(基数8)

只有字符0到7是有效的。八进制数值通过前缀"0"(零)来表示。

示例代码:

n = 0101; // 等同于十进制的65 ((1 * 8^2) + (0 * 8^1) + 1)

通过(无意中)在常量前加上一个前导零,并且让编译器无意中将你的常量解释为八进制,有可能产生一个难以发现的错误。

十六进制(基数16)

有效字符是0到9以及字母A到F;A的值是10,B是11,直到F,即15。十六进制值通过前缀"0x"来表示。注意A-F可以是大写(A-F)或小写(a-f)。

示例代码:

n = 0x101;  // 等同于十进制的257 ((1 * 16^2) + (0 * 16^1) + 1)

注释和警告

U和L格式化器:

默认情况下,一个整型常量被当作一个int,伴随着值的限制。要指定一个具有另一数据类型的整型常量,其后面可以跟随:

  • 一个'u'或'U'来强制常量进入无符号数据格式。例如:33u
  • 一个'l'或'L'来强制常量进入长整型数据格式。例如:100000L
  • 一个'ul'或'UL'来强制常量进入无符号长整型常量。例如:32767ul

See also

LED_BUILTIN

LED_BUILTIN

定义内置变量:LED_BUILTIN

大多数Arduino板都有一个引脚通过电阻与板载LED相连。常量LED_BUILTIN是与板载LED相连的引脚编号。大部分板的这个LED连接到数字引脚13。

See also

true | false

true | false

在Arduino语言中用来表示真和假的有两个常量:truefalse

true

true常常被定义为1,这是正确的,但true的定义更广泛。任何非零的整数在布尔意义上都是真的。因此,-1、2和-200在布尔意义上也都被定义为真。

请注意,与HIGHLOWINPUTOUTPUT不同,truefalse常量是用小写字母输入的。

false

false是这两个中更容易定义的。false被定义为0(零)。

请注意,与HIGHLOWINPUTOUTPUT不同,truefalse常量是用小写字母输入的。

See also

转换

数据类型

array

array

Description

数组是一系列变量的集合,可以通过索引号来访问。在 C++ 编程语言中编写的 Arduino 程序中的数组可能会很复杂,但使用简单数组相对直接。

创建(声明)数组

以下所有方法都是创建(声明)数组的有效方式。

  // 声明一个给定长度的数组,但不初始化值:
  int myInts[6];

  // 声明一个数组而不显式选择大小(编译器
  // 计算元素数量并创建适当大小的数组):
  int myPins[] = {2, 4, 8, 3, 6, 4};

  // 声明一个给定长度的数组并初始化其值:
  int mySensVals[5] = {2, 4, -8, 3, 2};

  // 声明 char 类型的数组时,你需要使其长度
  // 多一个元素以容纳所需的空终止字符:
  char message[6] = "hello";

访问数组

数组是从零开始索引的,也就是说,参考上面的数组初始化,数组的第一个元素位于索引 0,因此

mySensVals[0] == 2, mySensVals[1] == 4, 等等。

这也意味着在一个有十个元素的数组中,索引九是最后一个元素。因此:

int myArray[10]={9, 3, 2, 4, 3, 2, 7, 8, 9, 11};
// myArray[9]    包含 11
// myArray[10]   是无效的并包含随机信息(其他内存地址)

因此,在访问数组时应该小心。访问数组的末尾之后(使用一个大于你声明的数组大小减 1 的索引号)是在读取用于其他目的的内存。从这些位置读取可能不会做太多事情,除了产生无效数据。随机写入内存位置绝对是个坏主意,通常会导致不愉快的结果,如崩溃或程序故障。这也可能是一个难以追踪的错误。

与 BASIC 或 JAVA 不同,C++ 编译器不会检查数组访问是否在你声明的数组大小的合法范围内。

给数组赋值:

mySensVals[0] = 10;

从数组中检索值:

x = mySensVals[4];

数组和 FOR 循环

数组通常在 for 循环内操作,其中循环计数器用作每个数组元素的索引。例如,要通过串行端口打印数组的元素,你可以这样做:

for (byte i = 0; i < 5; i = i + 1) {
  Serial.println(myPins[i]);
}

Example Code

有关使用数组的完整程序示例,请参见(如何使用数组示例) 来自(内置示例)。

See also

bool

bool

Description

bool 可以持有两个值之一,truefalse。(每个 bool 变量占用一字节内存。)

Syntax

bool var = val;

Parameters

var:变量名。
val:要赋给该变量的值。

Example Code

此代码展示了如何使用 bool 数据类型。

int LEDpin = 5;     // LED 在引脚 5
int switchPin = 13; // 瞬时开关在 13,另一端连接到地线

bool running = false;

void setup() {
  pinMode(LEDpin, OUTPUT);
  pinMode(switchPin, INPUT);
  digitalWrite(switchPin, HIGH);  // 打开上拉电阻
}

void loop() {
  if (digitalRead(switchPin) == LOW) {
    // 开关被按下 - 上拉电阻通常保持引脚高电平
    delay(100);                     // 延迟以消抖开关
    running = !running;             // 切换 running 变量
    digitalWrite(LEDpin, running);  // 通过 LED 指示
  }
}

See also

boolean

boolean

Description

boolean 是 Arduino 定义的 bool 的非标准类型别名。建议使用标准类型 bool,二者是相同的。

See also

string

string

Description

文本字符串可以用两种方式表示。你可以使用 String 数据类型,或者你可以用一个以 null 结尾的 char 类型数组构建一个字符串。本页描述了后一种方法。有关 String 对象的更多详细信息,它提供了更多功能,但代价是更多内存,请参阅 String 对象页面。

Syntax

以下所有声明对于字符串都是有效的。

char Str1[15];
char Str2[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o'};
char Str3[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o', '\0'};
char Str4[] = "arduino";
char Str5[8] = "arduino";
char Str6[15] = "arduino";

声明字符串的可能性

  • 声明一个 char 数组,不进行初始化,如 Str1
  • 声明一个 char 数组(多一个 char),编译器会添加所需的空字符,如 Str2
  • 显式添加空字符,Str3
  • 用双引号中的字符串常量初始化;编译器会将数组大小设置为适合字符串常量加一个结尾空字符,Str4
  • 用显式大小和字符串常量初始化数组,Str5
  • 初始化数组,为更大的字符串留出额外空间,Str6

Null 结尾

通常,字符串以空字符(ASCII 码 0)结尾。这允许函数(如 Serial.print())告知字符串的结尾在哪里。否则,它们将继续读取实际上不属于字符串的内存后续字节。

这意味着你的字符串需要为包含的文本预留一个额外的字符位置。这就是为什么 Str2 和 Str5 需要八个字符,即使 "arduino" 只有七个 - 最后一个位置会自动填充空字符。Str4 将自动调整大小为八个字符,其中一个用于额外的空字符。在 Str3 中,我们自己显式包含了空字符(写为 '\0')。

请注意,可能会有一个没有最终空字符的字符串(例如,如果你将 Str2 的长度指定为七而不是八)。这将破坏大多数使用字符串的函数,所以你不应该有意这样做。但是,如果你注意到某些行为很奇怪(对不属于字符串的字符进行操作),这可能就是问题所在。

单引号还是双引号?

字符串总是用双引号("Abc")定义,字符总是用单引号('A')定义。

换行长字符串

你可以这样换行长字符串:

char myString[] = "这是第一行"
" 这是第二行"
" 等等";

字符串数组

在处理大量文本时,例如带有 LCD 显示屏的项目,设置一个字符串数组通常是很方便的。由于字符串本身就是数组,这实际上是一个二维数组的示例。

在下面的代码中,数据类型 char 之后的星号 "char*" 表示这是一个 "指针" 数组。所有数组名称实际上都是指针,因此这是创建数组数组所需的。指针是 C++ 中初学者较难理解的一个晦涩部分,但在这里使用它们而无需详细了解指针的工作原理。

Example Code

char *myStrings[] = {"这是字符串 1", "这是字符串 2", "这是字符串 3",
                     "这是字符串 4", "这是字符串 5", "这是字符串 6"
                    };

void setup() {
  Serial.begin(9600);
}

void loop() {
  for (int i = 0; i < 6; i++) {
    Serial.println(myStrings[i]);
    delay(500);
  }
}

See also

String()

Description

构造 String 类的实例。有多个版本可以从不同的数据类型构造 String (即将它们格式化为字符序列),包括:

  • 用双引号括起来的常量字符串(即字符数组)
  • 用单引号括起来的单个常量字符
  • 另一个 String 对象实例
  • 一个常量整数或长整型数
  • 使用指定进制的常量整数或长整型数
  • 一个整数或长整型变量
  • 使用指定进制的整数或长整型变量
  • 使用指定小数位数的浮点数或双精度浮点数

从数字构造 String 会得到一个包含该数字 ASCII 表示的字符串。默认使用十进制,所以

String thisString = String(13);

会得到字符串 "13"。不过你也可以使用其他进制。例如,

String thisString = String(13, HEX);

会得到字符串 "d",这是十进制值 13 的十六进制表示。或者如果你更喜欢二进制,

String thisString = String(13, BIN);

会得到字符串 "1101",这是 13 的二进制表示。

Syntax
String(val)
String(val, base)
String(val, decimalPlaces)
Parameters

val: 要格式化为 String 的变量。允许的数据类型: string、char、byte、int、long、unsigned int、unsigned long、float、double。
base: (可选) 用于格式化整数值的进制。
decimalPlaces: 仅当 val 为 float 或 double 时。所需的小数位数。

Returns

String 类的一个实例。

Example Code

以下都是 String 的有效声明。

String stringOne = "Hello String";                    // 使用常量字符串
String stringOne = String('a');                       // 将常量字符转换为 String
String stringTwo = String("This is a string");        // 将常量字符串转换为 String 对象
String stringOne = String(stringTwo + " with more");  // 连接两个字符串
String stringOne = String(13);                        // 使用常量整数
String stringOne = String(analogRead(0), DEC);        // 使用整数和进制
String stringOne = String(45, HEX);                   // 使用整数和进制(十六进制)
String stringOne = String(255, BIN);                  // 使用整数和进制(二进制)
String stringOne = String(millis(), DEC);             // 使用长整型和进制
String stringOne = String(5.698, 3);                  // 使用浮点数和小数位数

Functions

请参阅《Arduino Language Reference-Variables-String()-Functions》


Operators

请参阅《Arduino Language Reference-Variables-String()-Operators》

See also

变量作用域与限定符

实用

PROGMEM

PROGMEM

Description

将常量数据只保留在闪存(程序)内存中,而不是在程序开始时复制到SRAM中。有关Arduino板上可用的各种内存类型的描述。

PROGMEM关键词是一个变量修饰符,它只应与pgmspace.h中定义的数据类型一起使用。它告诉编译器“将这些信息仅保留在闪存内存中”,而不是在启动时复制到SRAM中,就像它通常会做的那样。

PROGMEM是pgmspace.h库的一部分。它会自动包含在Arduino IDE中。

虽然PROGMEM可以用于单个变量,但真正值得这么做的是当你有一大块需要存储的数据时,通常最容易在数组中存储,(或其他超出我们当前讨论范围的C++数据结构)。

使用PROGMEM是一个两步程序。一旦变量被定义为PROGMEM,就不能像常规的基于SRAM的变量那样读取:你必须使用在pgmspace.h中也定义的特定函数来读取它。

==== 重要提示!

PROGMEM在使用AVR板(Uno Rev3, Leonardo等)时有用。较新的板(Due, MKR WiFi 1010, GIGA R1 WiFi等)在变量声明为const时自动使用程序空间。然而,为了向后兼容,PROGMEM仍可以与新板一起使用。这个实现当前存在这里

Syntax

const dataType variableName[] PROGMEM = {data0, data1, data3…};

注意,因为PROGMEM是一个变量修饰符,关于它应该放在哪里没有硬性规定,所以Arduino编译器接受下面所有的定义,这些定义也是同义的。然而,实验表明,在不同版本的Arduino(与GCC版本有关)中,PROGMEM可能在一个位置工作而在另一个位置不工作。“字符串表”示例已经经过测试,可与Arduino 13一起使用。如果PROGMEM包含在变量名之后,较早版本的IDE可能会更好。

const dataType variableName[] PROGMEM = {}; // 使用这种形式
const PROGMEM dataType variableName[] = {}; // 或这种
const dataType PROGMEM variableName[] = {}; // 不用这种

Parameters

dataType: 允许的数据类型:任何变量类型。
variableName: 数据数组的名称。

Example Code

以下代码片段说明了如何读写保存在PROGMEM中的无符号字符(字节)和整数(2字节)。

// 保存一些无符号整数
const PROGMEM uint16_t charSet[] = { 65000, 32796, 16843, 10, 11234};

// 保存一些字符
const char signMessage[] PROGMEM = {"I AM PREDATOR,  UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};

unsigned int displayInt;
char myChar;


void setup() {
  Serial.begin(9600);
  while (!Serial);  // 等待串口连接。对于原生USB来说是必需的

  // 将你的设置代码放在这里,以便一次性运行:
  // 读回一个2字节的整型
  for (byte k = 0; k < 5; k++) {
    displayInt = pgm_read_word_near(charSet + k);
    Serial.println(displayInt);
  }
  Serial.println();

  // 读回一个字符
  int signMessageLength = strlen_P(signMessage);
  for (byte k = 0; k < signMessageLength; k++) {
    myChar = pgm_read_byte_near(signMessage + k);
    Serial.print(myChar);
  }

  Serial.println();
}

void loop() {
  // 将你的主代码置于此,以便重复运行:
}

字符串数组

在处理大量文本时,例如涉及LCD的项目,设置字符串数组会很方便。因为字符串本身就是数组,这实际上是一个二维数组的例子。

这些通常是大型结构,所以将它们放入程序内存中通常是可取的。下面的代码说明了这个想法。

/*
  PROGMEM字符串演示
  如何在程序内存(闪存)中存储字符串表,
  并检索它们。

  信息汇总自:
  http://www.nongnu.org/avr-libc/user-manual/pgmspace.html

  在程序内存中设置字符串表(数组)有点复杂,但
  这里有一个好模板可以遵循。

  设置字符串是一个两步过程。首先,定义字符串。
*/

#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "String 0"; // "String 0"等是要存储的字符串 - 根据需要更改。
const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";
const char string_4[] PROGMEM = "String 4";
const char string_5[] PROGMEM = "String 5";


// 然后设置一个表来引用你的字符串。

const char *const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5};

char buffer[30];  // 确保这足够大,可以容纳它必须持有的最大字符串

void setup() {
  Serial.begin(9600);
  while (!Serial);  // 等待串口连接。对于原生USB来说是必需的
  Serial.println("OK");
}


void loop() {
  /* 使用程序内存中的字符串表需要使用特殊函数来检索数据。
     strcpy_P函数将程序空间的字符串复制到RAM中的字符串("buffer")。
     确保你的RAM中接收字符串足够大,以容纳
     你从程序空间检索的任何内容。 */


  for (int i = 0; i < 6; i++) {
    strcpy_P(buffer, (char *)pgm_read_ptr(&(string_table[i])));  // 需要的强制转换和解引用,只需复制。
    Serial.println(buffer);
    delay(500);
  }
}

Notes and Warnings

请注意,变量必须是全局定义的,或者使用static关键词定义,才能与PROGMEM一起工作。

以下代码在函数内部不会工作:

const char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n";

即便是在函数内局部定义,以下代码也会工作:

const static char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n"

F()

当使用像:

Serial.print("Write something on the Serial Monitor");

这样的指令时,要打印的字符串通常保存在RAM中。如果你的草稿在串行监视器上打印了很多东西,你可以很容易地填满RAM。这可以通过不在启动时从FLASH内存空间加载字符串来避免。你可以很容易地表明字符串不被复制到RAM中,使用以下语法:

Serial.print(F("Write something on the Serial Monitor that is stored in FLASH"));

See also

最后修改:2024 年 04 月 29 日
如果觉得我的文章对你有用,请随意赞赏