arduino开发ESP8266学习笔记六——变量和函数
bool b1=0;//布尔值,只有0和1,使用“!” 取反操作
byte be;//一个字节存储8位无符号数,范围0-255
int i; //整数,占用两个字节,整数范围-32767~+32767
long a;//长整型,占四个字节,范围-2147483647~2147483647
float b;//浮点类型,
char c='A';
String d="hello world";//字符串
unsigned int e;//无符号整型,范围0-65535
unsigned long f;//无符号长整型,范围0-4294967295
const int g=1;//表示为常量整数,常亮定义后,不能对他的值进行更改。
Arduino 语法手册
http://wiki.geek-workshop.com/doku.php?id=arduino:arduino_language_reference
变量部分
常量constants
HIGH|LOW
INPUT|OUTPUT
true | false
constants是在Arduino语言里预定义的变量。它们被用来使程序更易阅读。我们按组将常量分类。
逻辑层定义,true与false(布尔Boolean常量)
在Arduino内有两个常量用来表示真和假:true和 false。
false
在这两个常量中false更容易被定义。false被定义为0(零)。
true
true通常被定义为1,这是正确的,但true具有更广泛的定义。在布尔含义(Boolean sense)里任何 非零 整数 为true。所以在布尔含义内-1,2和-200都定义为ture。
需要注意的是true和false常量,不同于HIGH,LOW,INPUT和OUTPUT,需要全部小写。
——这里引申一下题外话arduino是大小写敏感语言(case sensitive)。
引脚电压定义,HIGH和LOW
当读取(read)或写入(write)数字引脚时只有两个可能的值: HIGH 和 LOW 。
HIGH
HIGH(参考引脚)的含义取决于引脚(pin)的设置,引脚定义为INPUT或OUTPUT时含义有所不同。
当一个引脚通过pinMode被设置为INPUT,并通过digitalRead读取(read)时。如果当前引脚的电压大于等于3V,微控制器将会返回为HIGH。引脚也可以通过pinMode被设置为INPUT,并通过digitalWrite设置为HIGH。输入引脚的值将被一个内在的20K上拉电阻 控制 在HIGH上,除非一个外部电路将其拉低到LOW。当一个引脚通过pinMode被设置为OUTPUT,并digitalWrite设置为HIGH时,引脚的电压应在5V。在这种状态下,它可以 输出电流 。例如,点亮一个通过一串电阻接地或设置为LOW的OUTPUT属性引脚的LED。
LOW
LOW的含义同样取决于引脚设置,引脚定义为INPUT或OUTPUT时含义有所不同。当一个引脚通过pinMode配置为INPUT,通过digitalRead设置为读取(read)时,如果当前引脚的电压小于等于2V,微控制器将返回为LOW。当一个引脚通过pinMode配置为OUTPUT,并通过digitalWrite设置为LOW时,引脚为0V。在这种状态下,它可以 倒灌 电流。例如,点亮一个通过串联电阻连接到+5V,或到另一个引脚配置为OUTPUT、HIGH的的LED。
数字引脚(Digital pins)定义,INPUT和OUTPUT
数字引脚当作 INPUT 或 OUTPUT都可以 。用pinMode()方法使一个数字引脚从INPUT到OUTPUT变化。
引脚(Pins)配置为输入(Inputs)
Arduino(Atmega)引脚通过pinMode()配置为 输入(INPUT) 即是将其配置在一个高阻抗的状态。配置为INPUT的引脚可以理解为引脚取样时对电路有极小的需求,即等效于在引脚前串联一个100兆欧姆(Megohms)的电阻。这使得它们非常利于读取传感器,而不是为LED供电。
引脚(Pins)配置为输出(Outputs)
引脚通过pinMode()配置为 输出(OUTPUT) 即是将其配置在一个低阻抗的状态。
这意味着它们可以为电路提供充足的电流。Atmega引脚可以向其他设备/电路提供(提供正电流positive current)或倒灌(提供负电流negative current)达40毫安(mA)的电流。这使得它们利于给LED供电,而不是读取传感器。输出(OUTPUT)引脚被短路的接地或5V电路上会受到损坏甚至烧毁。Atmega引脚在为继电器或电机供电时,由于电流不足,将需要一些外接电路来实现供电。
integer constants
整数常量
整数常量是直接在程序中使用的数字,如123。默认情况下,这些数字被视为int,但你可以通过U和L修饰符进行更多的限制(见下文)。通常情况下,整数常量默认为十进制,但可以加上特殊前缀表示为其他进制。
进制 |
例子 |
格式 |
备注 |
10(十进制) |
123 |
无 |
|
2(二进制) |
B1111011 |
前缀'B' |
只适用于8位的值(0到255)字符0-1有效 |
8(八进制) |
0173 |
前缀”0” |
字符0-7有效 |
16(十六进制) |
0x7B |
前缀”0x” |
字符0-9,A-F,A-F有效 |
小数是十进制数。这是数学常识。如果一个数没有特定的前缀,则默认为十进制。
二进制以2为基底,只有数字0和1是有效的。
示例:
101 //和十进制5等价 (1*2^2 + 0*2^1 + 1*2^0)
二进制格式只能是8位的,即只能表示0-255之间的数。如果输入二进制数更方便的话,你可以用以下的方式:
myInt = (B11001100 * 256) + B10101010; // B11001100 作为高位。
八进制是以8为基底,只有0-7是有效的字符。前缀“0”(数字0)表示该值为八进制。
0101 // 等同于十进制数65 ((1 * 8^2) + (0 * 8^1) + 1)
警告:八进制数0前缀很可能无意产生很难发现的错误,因为你可能不小心在常量前加了个“0”,结果就悲剧了。
十六进制以16为基底,有效的字符为0-9和A-F。十六进制数用前缀“0x”(数字0,字母爱克斯)表示。请注意,A-F不区分大小写,就是说你也可以用a-f。
示例:
0x101 // 等同于十进制257 ((1 * 16^2) + (0 * 16^1) + 1)
U & L 格式
默认情况下,整型常量被视作int型。要将整型常量转换为其他类型时,请遵循以下规则:
'u' or 'U' 指定一个常量为无符号型。(只能表示正数和0) 例如: 33u
'l' or 'L' 指定一个常量为长整型。(表示数的范围更广) 例如: 100000L
'ul' or 'UL' 这个你懂的,就是上面两种类型,称作无符号长整型。 例如:32767ul
floating point constants
浮点常量
和整型常量类似,浮点常量可以使得代码更具可读性。浮点常量在编译时被转换为其表达式所取的值。
例子
n = .005; 浮点数可以用科学记数法表示。'E'和'e'都可以作为有效的指数标志。
浮点数 |
被转换为 |
被转换为 |
10.0 |
|
10 |
2.34E5 |
2.34 * 10^5 |
234000 |
67E-12 |
67.0 * 10^-12 |
0.000000000067 |
数据类型
void
void
void只用在函数声明中。它表示该函数将不会被返回任何数据到它被调用的函数中。
例子
//功能在“setup”和“loop”被执行
//但没有数据被返回到高一级的程序中
void setup()
{
// ...
}
void loop()
{
// ...
}
boolean
布尔
一个布尔变量拥有两个值,true或false。(每个布尔变量占用一个字节的内存。)
例子
int LEDpin = 5; // LED与引脚5相连
int switchPin = 13; // 开关的一个引脚连接引脚13,另一个引脚接地。
boolean 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
}
}
char
描述
一个数据类型,占用1个字节的内存存储一个字符值。字符都写在单引号,如'A'(多个字符(字符串)使用双引号,如“ABC”)。
字符以编号的形式存储。你可以在ASCII表中看到对应的编码。这意味着字符的ASCII值可以用来作数学计算。(例如'A'+ 1,因为大写A的ASCII值是65,所以结果为66)。如何将字符转换成数字参考serial.println命令。
char数据类型是有符号的类型,这意味着它的编码为-128到127。对于一个无符号一个字节(8位)的数据类型,使用byte数据类型。
例如
char myChar = 'A';
char myChar = 65; // both are equivalent
unsigned char
unsigned char
描述
一个无符号数据类型占用1个字节的内存。与byte的数据类型相同。
无符号的char数据类型能编码0到255的数字。
为了保持Arduino的编程风格的一致性,byte数据类型是首选。
例子
unsigned char myChar = 240;
byte
byte
描述
一个字节存储8位无符号数,从0到255。
例子
byte b = B10010; // "B" 是二进制格式(B10010等于十进制18)
int
int
简介
整数是基本数据类型,占用2字节。整数的范围为-32,768到32,767( -2^15 ~(2^15)-1)。
整数类型使用2的补码方式存储负数。最高位通常为符号位,表示数的正负。其余位被“取反加1”(此处请参考补码相关资料,不再赘述)。
Arduino为您处理负数计算问题,所以数学计算对您是透明的(术语:实际存在,但不可操作。相当于“黑盒”)。但是,当处理右移位运算符(»)时,可能有未预期的编译过程。
示例
int ledPin = 13;
语法
int var = val;
var - 变量名
val - 赋给变量的值
提示
当变量数值过大而超过整数类型所能表示的范围时(-32,768到32,767),变量值会“回滚”(详情见示例)。
int x
x = -32,768;
x = x - 1; // x 现在是 32,767。
x = 32,767;
x = x + 1; // x 现在是 -32,768。
unsigned int
描述
unsigned int(无符号整型)与整型数据同样大小,占据2字节。它只能用于存储正数而不能存储负数,范围0~65,535 (2^16) - 1)。
无符号整型和整型最重要的区别是它们的最高位不同,既符号位。在Arduino整型类型中,如果最高位是1,则此数被认为是负数,剩下的15位为按2的补码计算所得值。
例子
unsigned int ledPin = 13;
语法
unsigned int var = val;
var - 无符号变量名称
val - 给变量所赋予的值
编程提示
当变量的值超过它能表示的最大值时它会“滚回”最小值,反向也会出现这种现象。
unsigned int x
x = 0;
x = x - 1; //x现在等于65535--向负数方向滚回
x = x + 1; //x现在等于0--滚回
word
word(字符)
描述
一个存储一个16字节无符号数的字符,取值范围从0到65535,与unsigned int相同。
例子
word w = 10000;
long
long
描述
长整数型变量是扩展的数字存储变量,它可以存储32位(4字节)大小的变量,从-2,147,483,648到2,147,483,647。
例子
long speedOfLight = 186000L; //参见整数常量‘L’的说明
语法
long var = val;
var - 长整型变量名
var - 赋给变量的值
unsigned long
描述
无符号长整型变量扩充了变量容量以存储更大的数据,它能存储32位(4字节)数据。与标准长整型不同无符号长整型无法存储负数,其范围从0到4,294,967,295(2 ^ 32 - 1)。
例子
unsigned long time;
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.print("Time: ");
time = millis();
//程序开始后一直打印时间
Serial.println(time);
//等待一秒钟,以免发送大量的数据
delay(1000);
}
语法
unsigned long var = val;
var - 你所定义的变量名
val - 给变量所赋的值
float
float
描述
float,浮点型数据,就是有一个小数点的数字。浮点数经常被用来近似的模拟连续值,因为他们比整数更大的精确度。浮点数的取值范围在3.4028235 E+38 ~ -3.4028235E +38。它被存储为32位(4字节)的信息。
float只有6-7位有效数字。这指的是总位数,而不是小数点右边的数字。与其他平台不同的是,在那里你可以使用double型得到更精确的结果(如15位),在Arduino上,double型与float型的大小相同。
浮点数字在有些情况下是不准确的,在数据大小比较时,可能会产生奇怪的结果。例如 6.0 / 3.0 可能不等于 2.0。你应该使两个数字之间的差额的绝对值小于一些小的数字,这样就可以近似的得到这两个数字相等这样的结果。
浮点运算速度远远慢于执行整数运算,例如,如果这个循环有一个关键的计时功能,并需要以最快的速度运行,就应该避免浮点运算。程序员经常使用较长的程式把浮点运算转换成整数运算来提高速度。
举例
float myfloat;
float sensorCalbrate = 1.117;
语法
float var = val;
var——您的float型变量名称
val——分配给该变量的值
示例代码
int x;
int y;
float z;
x = 1;
y = x / 2; // Y为0,因为整数不能容纳分数
z = (float)x / 2.0; // Z为0.5(你必须使用2.0做除数,而不是2)
double
double
描述
双精度浮点数。占用4个字节。
目前的arduino上的double实现和float相同,精度并未提高。
提示
如果你从其他地方得到的代码中包含了double类变量,最好检查一遍代码以确认其中的变量的精确度能否在arduino上达到。
string(字符串)
描述
文本字符串可以有两种表现形式。你可以使用字符串数据类型(这是0019版本的核心部分),或者你可以做一个字符串,由char类型的数组和空终止字符('\0')构成。(求助,待润色-Leo)本节描述了后一种方法。而字符串对象(String object)将让你拥有更多的功能,同时也消耗更多的内存资源,关于它的详细信息,请参阅页面(String object)[超链接]
举例
以下所有字符串都是有效的声明。
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";
声明字符串的解释
在Str1中 声明一个没有初始化的字符数组
在Str2中 声明一个字符数组(包括一个附加字符),编译器会自动添加所需的空字符
在Str3中 明确加入空字符
在Str4中 用引号分隔初始化的字符串常数,编译器将调整数组的大小,以适应字符串常量和终止空字符
在Str5中 初始化一个包括明确的尺寸和字符串常量的数组
在Str6中 初始化数组,预留额外的空间用于一个较大的字符串
空终止字符
一般来说,字符串的结尾有一个空终止字符(ASCII代码0)。以此让功能函数(例如Serial.pring())知道一个字符串的结束。否则,他们将从内存继续读取后续字节,而这些并不属于所需字符串的一部分。
这意味着,你的字符串比你想要的文字包含更多的个字符空间。这就是为什么Str2和Str5需要八个字符,即使“Arduino”只有七个字符 - 最后一个位置会自动填充空字符。str4将自动调整为八个字符,包括一个额外的空。在Str3的,我们自己已经明确地包含了空字符(写入'\ 0')。
需要注意的是,字符串可能没有一个最后的空字符(例如在Str2中您已定义字符长度为7,而不是8)。这会破坏大部分使用字符串的功能,所以不要故意而为之。如果你注意到一些奇怪的现象(在字符串中操作字符),基本就是这个原因导致的了。
单引号?还是双引号?
定义字符串时使用双引号(例如“ABC”),而定义一个单独的字符时使用单引号(例如'A')
包装长字符串
你可以像这样打包长字符串: char myString[] = “This is the first line” ” this is the second line” ” etcetera”;
字符串数组
当你的应用包含大量的文字,如带有液晶显示屏的一个项目,建立一个字符串数组是非常便利的。因为字符串本身就是数组,它实际上是一个两维数组的典型。
在下面的代码,”char*”在字符数据类型char后跟了一个星号'*'表示这是一个“指针”数组。所有的数组名实际上是指针,所以这需要一个数组的数组。指针对于C语言初学者而言是非常深奥的部分之一,但我们没有必要了解详细指针,就可以有效地应用它。
样例
char* myStrings[]={
"This is string 1", "This is string 2", "This is string 3",
"This is string 4", "This is string 5","This is string 6"};
void setup(){
Serial.begin(9600);
}
void loop(){
for (int i = 0; i < 6; i++){
Serial.println(myStrings[i]);
delay(500);
}
}
String - object
描述
String类,是0019版的核心的一部分,允许你实现比运用字符数组更复杂的文字操作。你可以连接字符串,增加字符串,寻找和替换子字符串以及其他操作。它比使用一个简单的字符数组需要更多的内存,但它更方便。
仅供参考,字符串数组都用小写的string表示而String类的实例通常用大写的String表示。注意,在“双引号”内指定的字符常量通常被作为字符数组,并非String类实例。
函数
String
charAt()
compareTo()
concat()
endsWith()
equals()
equalsIgnoreCase()
GetBytes()
indexOf()
lastIndexOf
length
replace()
setCharAt()
startsWith()
substring()
toCharArray()
toLowerCase()
toUpperCase()
trim()
操作符
[](元素访问)
+(串连)
==(比较)
举例
StringConstructors
StringAdditionOperator
StringIndexOf
StringAppendOperator
StringLengthTrim
StringCaseChanges
StringReplace
StringCharacters
StringStartsWithEndsWith
StringComparisonOperators
StringSubstring
array -(数组)
Arrays (数组)
数组是一种可访问的变量的集合。Arduino的数组是基于C语言的,因此这会变得很复杂,但使用简单的数组是比较简单的。
创建(声明)一个数组
下面的方法都可以用来创建(声明)数组。
myInts [6];
myPins [] = {2,4,8,3,6};
mySensVals [6] = {2,4,-8,3,2};
char message[6] = "hello";
你声明一个未初始化数组,例如myPins。
在myPins中,我们声明了一个没有明确大小的数组。编译器将会计算元素的大小,并创建一个适当大小的数组。
当然,你也可以初始化数组的大小,例如在mySensVals中。请注意,当声明一个char类型的数组时,你初始化的大小必须大于元素的个数,以容纳所需的空字符。
访问数组
数组是从零开始索引的,也就说,上面所提到的数组初始化,数组第一个元素是为索引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循环中进行操作,循环计数器可用于访问每个数组元素。例如,将数组中的元素通过串口打印,你可以这样做:
int i;
for (i = 0; i < 5; i = i + 1) {
Serial.println(myPins[i]);
}
数据类型转换
char()
char
描述
将一个变量的类型变为char。
语法
char(x)
参数
x:任何类型的值
返回
char
byte()
byte()
描述
将一个值转换为字节型数值。
语法
byte(x)
参数
X:任何类型的值
返回
字节
int()
简介
将一个值转换为int类型。
语法
int(x)
参数
x:一个任何类型的值
返回值
int类型的值
word()
描述:把一个值转换为word数据类型的值,或由两个字节创建一个字符。
语法:word(x)
word(h, l)
参数:X:任何类型的值
H:高阶(最左边)字节
L:低序(最右边)字节
返回:字符
long()
描述:将一个值转换为长整型数据类型。
语法:long(x)
参数:x:任意类型的数值
返回:长整型数
float()
描述:将一个值转换为float型数值。
语法:float(x)
参数:X:任何类型的值
返回:float型数
注释:见float中关于Arduino浮点数的精度和限制的详细信息。
变量作用域 & 修饰符
variable scope
变量的作用域
在Arduino使用的C编程语言的变量,有一个名为 作用域(scope) 的属性 。这一点与类似BASIC的语言形成了对比,在BASIC语言中所有变量都是 全局(global) 变量。
在一个程序内的全局变量是可以被所有函数所调用的。局部变量只在声明它们的函数内可见。在Arduino的环境中,任何在函数(例如,setup(),loop()等)外声明的变量,都是全局变量。
当程序变得更大更复杂时,局部变量是一个有效确定每个函数只能访问其自己变量的途径。这可以防止,当一个函数无意中修改另一个函数使用的变量的程序错误。
有时在一个for循环内声明并初始化一个变量也是很方便的选择。这将创建一个只能从for循环的括号内访问的变量。
例子:
int gPWMval; // 任何函数都可以调用此变量
void setup()
{
// ...
}
void loop()
{
int i; // "i" 只在 "loop" 函数内可用
float f; // "f" 只在 "loop" 函数内可用
// ...
for (int j = 0; j <100; j++){
//变量j只能在循环括号内访问
}
}
static
static关键字用于创建只对某一函数可见的变量。然而,和局部变量不同的是,局部变量在每次调用函数时都会被创建和销毁,静态变量在函数调用后仍然保持着原来的数据。
静态变量只会在函数第一次调用的时候被创建和初始化。
例子
/* RandomWalk
* Paul Badger 2007
* RandomWalk函数在两个终点间随机的上下移动
* 在一个循环中最大的移动由参数“stepsize”决定
*一个静态变量向上和向下移动一个随机量
*这种技术也被叫做“粉红噪声”或“醉步”
*/
#define randomWalkLowRange -20
#define randomWalkHighRange 20
int stepsize;
INT thisTime;
int total;
void setup()
{
Serial.begin(9600);
}
void loop()
{ // 测试randomWalk 函数
stepsize = 5;
thisTime = randomWalk(stepsize);
serial.println(thisTime);
delay(10);
}
int randomWalk(int moveSize){
static int place; // 在randomwalk中存储变量
// 声明为静态因此它在函数调用之间能保持数据,但其他函数无法改变它的值
place = place + (random(-moveSize, moveSize + 1));
if (place < randomWalkLowRange){ //检查上下限
place = place + (randomWalkLowRange - place); // 将数字变为正方向
}
else if(place > randomWalkHighRange){
place = place - (place - randomWalkHighRange); // 将数字变为负方向
}
return place;
}
volatile
volatile关键字
volatile这个关键字是变量修饰符,常用在变量类型的前面,以告诉编译器和接下来的程序怎么对待这个变量。
声明一个volatile变量是编译器的一个指令。编译器是一个将你的C/C++代码转换成机器码的软件,机器码是arduino上的Atmega芯片能识别的真正指令。
具体来说,它指示编译器编译器从RAM而非存储寄存器中读取变量,存储寄存器是程序存储和操作变量的一个临时地方。在某些情况下,存储在寄存器中的变量值可能是不准确的。
如果一个变量所在的代码段可能会意外地导致变量值改变那此变量应声明为volatile,比如并行多线程等。在arduino中,唯一可能发生这种现象的地方就是和中断有关的代码段,成为中断服务程序。
例子
//当中断引脚改变状态时,开闭LED
int pin = 13;
volatile int state = LOW;
void setup()
{
pinMode(pin, OUTPUT);
attachInterrupt(0, blink, CHANGE);
}
void loop()
{
digitalWrite(pin, state);
}
void blink()
{
state = !state;
}
const
const关键字
const关键字代表常量。它是一个变量限定符,用于修改变量的性质,使其变为只读状态。这意味着该变量,就像任何相同类型的其他变量一样使用,但不能改变其值。如果尝试为一个const变量赋值,编译时将会报错。
const关键字定义的常量,遵守 variable scoping 管辖的其他变量的规则。这一点加上使用 #define的缺陷 ,使 const 关键字成为定义常量的一个的首选方法。
例子
const float pi = 3.14;
float x;
// ....
x = pi * 2; // 在数学表达式中使用常量不会报错
pi = 7; // 错误的用法 - 你不能修改常量值,或给常量赋值。
#define 或 const
您可以使用 const 或 #define 创建数字或字符串常量。但 arrays, 你只能使用 const。 一般 const 相对 的#define是首选 的定义常量语法。
辅助工具
sizeof()
描述
sizeof操作符返回一个变量类型的字节数,或者该数在数组中占有的字节数。
语法
sizeof(variable)
参数
variable: 任何变量类型或数组(如int,float,byte)
示例代码
sizeof操作符用来处理数组非常有效,它能很方便的改变数组的大小而不用破坏程序的其他部分。
这个程序一次打印出一个字符串文本的字符。尝试改变一下字符串。
char myStr[] = "this is a test";
int i;
void setup(){
Serial.begin(9600);
}
{0}void{/0}{1} {/1}{2}loop{/2}{1}() {{/1}
for (i = 0; i < sizeof(myStr) - 1; i++){
Serial.print(i, DEC);
Serial.print(" = ");
Serial.println(myStr[i], BYTE);
}
}
请注意sizeof返回字节数总数。因此,较大的变量类型,如整数,for循环看起来应该像这样。
for (i = 0; i < (sizeof(myInts)/sizeof(int)) - 1; i++) {
//用myInts[i]来做些事
}
Arduino 语法手册
http://wiki.geek-workshop.com/doku.php?id=arduino:arduino_language_reference
变量部分
常量constants
HIGH|LOW
INPUT|OUTPUT
true | false
constants是在Arduino语言里预定义的变量。它们被用来使程序更易阅读。我们按组将常量分类。
在Arduino内有两个常量用来表示真和假:true和 false。
在这两个常量中false更容易被定义。false被定义为0(零)。
true通常被定义为1,这是正确的,但true具有更广泛的定义。在布尔含义(Boolean sense)里任何 非零 整数 为true。所以在布尔含义内-1,2和-200都定义为ture。需要注意的是true和false常量,不同于HIGH,LOW,INPUT和OUTPUT,需要全部小写。
——这里引申一下题外话arduino是大小写敏感语言(case sensitive)。
当读取(read)或写入(write)数字引脚时只有两个可能的值: HIGH 和 LOW 。
HIGH(参考引脚)的含义取决于引脚(pin)的设置,引脚定义为INPUT或OUTPUT时含义有所不同。当一个引脚通过pinMode被设置为INPUT,并通过digitalRead读取(read)时。如果当前引脚的电压大于等于3V,微控制器将会返回为HIGH。引脚也可以通过pinMode被设置为INPUT,并通过digitalWrite设置为HIGH。输入引脚的值将被一个内在的20K上拉电阻 控制 在HIGH上,除非一个外部电路将其拉低到LOW。当一个引脚通过pinMode被设置为OUTPUT,并digitalWrite设置为HIGH时,引脚的电压应在5V。在这种状态下,它可以 输出电流 。例如,点亮一个通过一串电阻接地或设置为LOW的OUTPUT属性引脚的LED。
LOW的含义同样取决于引脚设置,引脚定义为INPUT或OUTPUT时含义有所不同。当一个引脚通过pinMode配置为INPUT,通过digitalRead设置为读取(read)时,如果当前引脚的电压小于等于2V,微控制器将返回为LOW。当一个引脚通过pinMode配置为OUTPUT,并通过digitalWrite设置为LOW时,引脚为0V。在这种状态下,它可以 倒灌 电流。例如,点亮一个通过串联电阻连接到+5V,或到另一个引脚配置为OUTPUT、HIGH的的LED。
数字引脚(Digital pins)定义,INPUT和OUTPUT
数字引脚当作 INPUT 或 OUTPUT都可以 。用pinMode()方法使一个数字引脚从INPUT到OUTPUT变化。
Arduino(Atmega)引脚通过pinMode()配置为 输入(INPUT) 即是将其配置在一个高阻抗的状态。配置为INPUT的引脚可以理解为引脚取样时对电路有极小的需求,即等效于在引脚前串联一个100兆欧姆(Megohms)的电阻。这使得它们非常利于读取传感器,而不是为LED供电。
引脚通过pinMode()配置为 输出(OUTPUT) 即是将其配置在一个低阻抗的状态。
这意味着它们可以为电路提供充足的电流。Atmega引脚可以向其他设备/电路提供(提供正电流positive current)或倒灌(提供负电流negative current)达40毫安(mA)的电流。这使得它们利于给LED供电,而不是读取传感器。输出(OUTPUT)引脚被短路的接地或5V电路上会受到损坏甚至烧毁。Atmega引脚在为继电器或电机供电时,由于电流不足,将需要一些外接电路来实现供电。
integer constants
整数常量是直接在程序中使用的数字,如123。默认情况下,这些数字被视为int,但你可以通过U和L修饰符进行更多的限制(见下文)。通常情况下,整数常量默认为十进制,但可以加上特殊前缀表示为其他进制。
进制 |
例子 |
格式 |
备注 |
10(十进制) |
123 |
无 |
|
2(二进制) |
B1111011 |
前缀'B' |
只适用于8位的值(0到255)字符0-1有效 |
8(八进制) |
0173 |
前缀”0” |
字符0-7有效 |
16(十六进制) |
0x7B |
前缀”0x” |
字符0-9,A-F,A-F有效 |
小数是十进制数。这是数学常识。如果一个数没有特定的前缀,则默认为十进制。
二进制以2为基底,只有数字0和1是有效的。
示例:
101 //和十进制5等价 (1*2^2 + 0*2^1 + 1*2^0)
二进制格式只能是8位的,即只能表示0-255之间的数。如果输入二进制数更方便的话,你可以用以下的方式:
myInt = (B11001100 * 256) + B10101010; // B11001100 作为高位。
八进制是以8为基底,只有0-7是有效的字符。前缀“0”(数字0)表示该值为八进制。
0101 // 等同于十进制数65 ((1 * 8^2) + (0 * 8^1) + 1)
警告:八进制数0前缀很可能无意产生很难发现的错误,因为你可能不小心在常量前加了个“0”,结果就悲剧了。
十六进制以16为基底,有效的字符为0-9和A-F。十六进制数用前缀“0x”(数字0,字母爱克斯)表示。请注意,A-F不区分大小写,就是说你也可以用a-f。
示例:
0x101 // 等同于十进制257 ((1 * 16^2) + (0 * 16^1) + 1)
默认情况下,整型常量被视作int型。要将整型常量转换为其他类型时,请遵循以下规则:
'u' or 'U' 指定一个常量为无符号型。(只能表示正数和0) 例如: 33u
'l' or 'L' 指定一个常量为长整型。(表示数的范围更广) 例如: 100000L
'ul' or 'UL' 这个你懂的,就是上面两种类型,称作无符号长整型。 例如:32767ul
floating point constants
和整型常量类似,浮点常量可以使得代码更具可读性。浮点常量在编译时被转换为其表达式所取的值。
例子
n = .005; 浮点数可以用科学记数法表示。'E'和'e'都可以作为有效的指数标志。
浮点数 |
被转换为 |
被转换为 |
10.0 |
|
10 |
2.34E5 |
2.34 * 10^5 |
234000 |
67E-12 |
67.0 * 10^-12 |
0.000000000067 |
数据类型
void
void只用在函数声明中。它表示该函数将不会被返回任何数据到它被调用的函数中。
例子
//功能在“setup”和“loop”被执行
//但没有数据被返回到高一级的程序中
void setup()
{
// ...
}
void loop()
{
// ...
}
boolean
一个布尔变量拥有两个值,true或false。(每个布尔变量占用一个字节的内存。)
例子
int LEDpin = 5; // LED与引脚5相连
int switchPin = 13; // 开关的一个引脚连接引脚13,另一个引脚接地。
boolean 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
}
}
char
描述
一个数据类型,占用1个字节的内存存储一个字符值。字符都写在单引号,如'A'(多个字符(字符串)使用双引号,如“ABC”)。
字符以编号的形式存储。你可以在ASCII表中看到对应的编码。这意味着字符的ASCII值可以用来作数学计算。(例如'A'+ 1,因为大写A的ASCII值是65,所以结果为66)。如何将字符转换成数字参考serial.println命令。
char数据类型是有符号的类型,这意味着它的编码为-128到127。对于一个无符号一个字节(8位)的数据类型,使用byte数据类型。
例如
char myChar = 'A';
char myChar = 65; // both are equivalent
unsigned char
描述
一个无符号数据类型占用1个字节的内存。与byte的数据类型相同。
无符号的char数据类型能编码0到255的数字。
为了保持Arduino的编程风格的一致性,byte数据类型是首选。
例子
unsigned char myChar = 240;
byte
描述
一个字节存储8位无符号数,从0到255。
例子
byte b = B10010; // "B" 是二进制格式(B10010等于十进制18)
int
整数是基本数据类型,占用2字节。整数的范围为-32,768到32,767( -2^15 ~(2^15)-1)。
整数类型使用2的补码方式存储负数。最高位通常为符号位,表示数的正负。其余位被“取反加1”(此处请参考补码相关资料,不再赘述)。
Arduino为您处理负数计算问题,所以数学计算对您是透明的(术语:实际存在,但不可操作。相当于“黑盒”)。但是,当处理右移位运算符(»)时,可能有未预期的编译过程。
示例
int ledPin = 13;
语法
int var = val;
var - 变量名
val - 赋给变量的值
提示
当变量数值过大而超过整数类型所能表示的范围时(-32,768到32,767),变量值会“回滚”(详情见示例)。
int x
x = -32,768;
x = x - 1; // x 现在是 32,767。
x = 32,767;
x = x + 1; // x 现在是 -32,768。
unsigned int
描述
unsigned int(无符号整型)与整型数据同样大小,占据2字节。它只能用于存储正数而不能存储负数,范围0~65,535 (2^16) - 1)。
无符号整型和整型最重要的区别是它们的最高位不同,既符号位。在Arduino整型类型中,如果最高位是1,则此数被认为是负数,剩下的15位为按2的补码计算所得值。
例子
unsigned int ledPin = 13;
语法
unsigned int var = val;
var - 无符号变量名称
val - 给变量所赋予的值
编程提示
当变量的值超过它能表示的最大值时它会“滚回”最小值,反向也会出现这种现象。
unsigned int x
x = 0;
x = x - 1; //x现在等于65535--向负数方向滚回
x = x + 1; //x现在等于0--滚回
word
描述
一个存储一个16字节无符号数的字符,取值范围从0到65535,与unsigned int相同。
例子
word w = 10000;
long
描述
长整数型变量是扩展的数字存储变量,它可以存储32位(4字节)大小的变量,从-2,147,483,648到2,147,483,647。
例子
long speedOfLight = 186000L; //参见整数常量‘L’的说明
语法
long var = val;
var - 长整型变量名
var - 赋给变量的值
unsigned long
描述
无符号长整型变量扩充了变量容量以存储更大的数据,它能存储32位(4字节)数据。与标准长整型不同无符号长整型无法存储负数,其范围从0到4,294,967,295(2 ^ 32 - 1)。
例子
unsigned long time;
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.print("Time: ");
time = millis();
//程序开始后一直打印时间
Serial.println(time);
//等待一秒钟,以免发送大量的数据
delay(1000);
}
语法
unsigned long var = val;
var - 你所定义的变量名
val - 给变量所赋的值
float
描述
float,浮点型数据,就是有一个小数点的数字。浮点数经常被用来近似的模拟连续值,因为他们比整数更大的精确度。浮点数的取值范围在3.4028235 E+38 ~ -3.4028235E +38。它被存储为32位(4字节)的信息。
float只有6-7位有效数字。这指的是总位数,而不是小数点右边的数字。与其他平台不同的是,在那里你可以使用double型得到更精确的结果(如15位),在Arduino上,double型与float型的大小相同。
浮点数字在有些情况下是不准确的,在数据大小比较时,可能会产生奇怪的结果。例如 6.0 / 3.0 可能不等于 2.0。你应该使两个数字之间的差额的绝对值小于一些小的数字,这样就可以近似的得到这两个数字相等这样的结果。
浮点运算速度远远慢于执行整数运算,例如,如果这个循环有一个关键的计时功能,并需要以最快的速度运行,就应该避免浮点运算。程序员经常使用较长的程式把浮点运算转换成整数运算来提高速度。
举例
float myfloat;
float sensorCalbrate = 1.117;
语法
float var = val;
var——您的float型变量名称
val——分配给该变量的值
示例代码
int x;
int y;
float z;
x = 1;
y = x / 2; // Y为0,因为整数不能容纳分数
z = (float)x / 2.0; // Z为0.5(你必须使用2.0做除数,而不是2)
double
描述
双精度浮点数。占用4个字节。
目前的arduino上的double实现和float相同,精度并未提高。
提示
如果你从其他地方得到的代码中包含了double类变量,最好检查一遍代码以确认其中的变量的精确度能否在arduino上达到。
string - char array
描述
文本字符串可以有两种表现形式。你可以使用字符串数据类型(这是0019版本的核心部分),或者你可以做一个字符串,由char类型的数组和空终止字符('\0')构成。(求助,待润色-Leo)本节描述了后一种方法。而字符串对象(String object)将让你拥有更多的功能,同时也消耗更多的内存资源,关于它的详细信息,请参阅页面(String object)[超链接]
举例
以下所有字符串都是有效的声明。
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";
在Str1中 声明一个没有初始化的字符数组
在Str2中 声明一个字符数组(包括一个附加字符),编译器会自动添加所需的空字符
在Str3中 明确加入空字符
在Str4中 用引号分隔初始化的字符串常数,编译器将调整数组的大小,以适应字符串常量和终止空字符
在Str5中 初始化一个包括明确的尺寸和字符串常量的数组
在Str6中 初始化数组,预留额外的空间用于一个较大的字符串
一般来说,字符串的结尾有一个空终止字符(ASCII代码0)。以此让功能函数(例如Serial.pring())知道一个字符串的结束。否则,他们将从内存继续读取后续字节,而这些并不属于所需字符串的一部分。
这意味着,你的字符串比你想要的文字包含更多的个字符空间。这就是为什么Str2和Str5需要八个字符,即使“Arduino”只有七个字符 - 最后一个位置会自动填充空字符。str4将自动调整为八个字符,包括一个额外的空。在Str3的,我们自己已经明确地包含了空字符(写入'\ 0')。
需要注意的是,字符串可能没有一个最后的空字符(例如在Str2中您已定义字符长度为7,而不是8)。这会破坏大部分使用字符串的功能,所以不要故意而为之。如果你注意到一些奇怪的现象(在字符串中操作字符),基本就是这个原因导致的了。
定义字符串时使用双引号(例如“ABC”),而定义一个单独的字符时使用单引号(例如'A')
你可以像这样打包长字符串: char myString[] = “This is the first line” ” this is the second line” ” etcetera”;
当你的应用包含大量的文字,如带有液晶显示屏的一个项目,建立一个字符串数组是非常便利的。因为字符串本身就是数组,它实际上是一个两维数组的典型。
在下面的代码,”char*”在字符数据类型char后跟了一个星号'*'表示这是一个“指针”数组。所有的数组名实际上是指针,所以这需要一个数组的数组。指针对于C语言初学者而言是非常深奥的部分之一,但我们没有必要了解详细指针,就可以有效地应用它。
char* myStrings[]={
"This is string 1", "This is string 2", "This is string 3",
"This is string 4", "This is string 5","This is string 6"};
void setup(){
Serial.begin(9600);
}
void loop(){
for (int i = 0; i < 6; i++){
Serial.println(myStrings[i]);
delay(500);
}
}
String - object
描述
String类,是0019版的核心的一部分,允许你实现比运用字符数组更复杂的文字操作。你可以连接字符串,增加字符串,寻找和替换子字符串以及其他操作。它比使用一个简单的字符数组需要更多的内存,但它更方便。
仅供参考,字符串数组都用小写的string表示而String类的实例通常用大写的String表示。注意,在“双引号”内指定的字符常量通常被作为字符数组,并非String类实例。
String
charAt()
compareTo()
concat()
endsWith()
equals()
equalsIgnoreCase()
GetBytes()
indexOf()
lastIndexOf
length
replace()
setCharAt()
startsWith()
substring()
toCharArray()
toLowerCase()
toUpperCase()
trim()
[](元素访问)
+(串连)
==(比较)
举例
StringConstructors
StringAdditionOperator
StringIndexOf
StringAppendOperator
StringLengthTrim
StringCaseChanges
StringReplace
StringCharacters
StringStartsWithEndsWith
StringComparisonOperators
StringSubstring
array -(数组)
数组是一种可访问的变量的集合。Arduino的数组是基于C语言的,因此这会变得很复杂,但使用简单的数组是比较简单的。
下面的方法都可以用来创建(声明)数组。
myInts [6];
myPins [] = {2,4,8,3,6};
mySensVals [6] = {2,4,-8,3,2};
char message[6] = "hello";
你声明一个未初始化数组,例如myPins。
在myPins中,我们声明了一个没有明确大小的数组。编译器将会计算元素的大小,并创建一个适当大小的数组。
当然,你也可以初始化数组的大小,例如在mySensVals中。请注意,当声明一个char类型的数组时,你初始化的大小必须大于元素的个数,以容纳所需的空字符。
数组是从零开始索引的,也就说,上面所提到的数组初始化,数组第一个元素是为索引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循环中进行操作,循环计数器可用于访问每个数组元素。例如,将数组中的元素通过串口打印,你可以这样做:
int i;
for (i = 0; i < 5; i = i + 1) {
Serial.println(myPins[i]);
}
数据类型转换
char()
描述
将一个变量的类型变为char。
语法
char(x)
参数
x:任何类型的值
返回
char
byte()
byte()
描述
将一个值转换为字节型数值。
语法
byte(x)
参数
X:任何类型的值
返回
字节
int()
简介
将一个值转换为int类型。
语法
int(x)
参数
x:一个任何类型的值
int类型的值
word()
描述:把一个值转换为word数据类型的值,或由两个字节创建一个字符。
语法:word(x)
word(h, l)
参数:X:任何类型的值
H:高阶(最左边)字节
L:低序(最右边)字节
返回:字符
long()
描述:将一个值转换为长整型数据类型。
语法:long(x)
参数:x:任意类型的数值
返回:长整型数
float()
描述:将一个值转换为float型数值。
语法:float(x)
参数:X:任何类型的值
返回:float型数
注释:见float中关于Arduino浮点数的精度和限制的详细信息。
变量作用域 & 修饰符
variable scope
在Arduino使用的C编程语言的变量,有一个名为 作用域(scope) 的属性 。这一点与类似BASIC的语言形成了对比,在BASIC语言中所有变量都是 全局(global) 变量。
在一个程序内的全局变量是可以被所有函数所调用的。局部变量只在声明它们的函数内可见。在Arduino的环境中,任何在函数(例如,setup(),loop()等)外声明的变量,都是全局变量。
当程序变得更大更复杂时,局部变量是一个有效确定每个函数只能访问其自己变量的途径。这可以防止,当一个函数无意中修改另一个函数使用的变量的程序错误。
有时在一个for循环内声明并初始化一个变量也是很方便的选择。这将创建一个只能从for循环的括号内访问的变量。
例子:
int gPWMval; // 任何函数都可以调用此变量
void setup()
{
// ...
}
void loop()
{
int i; // "i" 只在 "loop" 函数内可用
float f; // "f" 只在 "loop" 函数内可用
// ...
for (int j = 0; j <100; j++){
//变量j只能在循环括号内访问
}
}
static
static关键字用于创建只对某一函数可见的变量。然而,和局部变量不同的是,局部变量在每次调用函数时都会被创建和销毁,静态变量在函数调用后仍然保持着原来的数据。
静态变量只会在函数第一次调用的时候被创建和初始化。
例子
/* RandomWalk
* Paul Badger 2007
* RandomWalk函数在两个终点间随机的上下移动
* 在一个循环中最大的移动由参数“stepsize”决定
*一个静态变量向上和向下移动一个随机量
*这种技术也被叫做“粉红噪声”或“醉步”
*/
#define randomWalkLowRange -20
#define randomWalkHighRange 20
int stepsize;
INT thisTime;
int total;
void setup()
{
Serial.begin(9600);
}
void loop()
{ // 测试randomWalk 函数
stepsize = 5;
thisTime = randomWalk(stepsize);
serial.println(thisTime);
delay(10);
}
int randomWalk(int moveSize){
static int place; // 在randomwalk中存储变量
// 声明为静态因此它在函数调用之间能保持数据,但其他函数无法改变它的值
place = place + (random(-moveSize, moveSize + 1));
if (place < randomWalkLowRange){ //检查上下限
place = place + (randomWalkLowRange - place); // 将数字变为正方向
}
else if(place > randomWalkHighRange){
place = place - (place - randomWalkHighRange); // 将数字变为负方向
}
return place;
}
volatile
volatile这个关键字是变量修饰符,常用在变量类型的前面,以告诉编译器和接下来的程序怎么对待这个变量。
声明一个volatile变量是编译器的一个指令。编译器是一个将你的C/C++代码转换成机器码的软件,机器码是arduino上的Atmega芯片能识别的真正指令。
具体来说,它指示编译器编译器从RAM而非存储寄存器中读取变量,存储寄存器是程序存储和操作变量的一个临时地方。在某些情况下,存储在寄存器中的变量值可能是不准确的。
如果一个变量所在的代码段可能会意外地导致变量值改变那此变量应声明为volatile,比如并行多线程等。在arduino中,唯一可能发生这种现象的地方就是和中断有关的代码段,成为中断服务程序。
例子
//当中断引脚改变状态时,开闭LED
int pin = 13;
volatile int state = LOW;
void setup()
{
pinMode(pin, OUTPUT);
attachInterrupt(0, blink, CHANGE);
}
void loop()
{
digitalWrite(pin, state);
}
void blink()
{
state = !state;
}
const
const关键字代表常量。它是一个变量限定符,用于修改变量的性质,使其变为只读状态。这意味着该变量,就像任何相同类型的其他变量一样使用,但不能改变其值。如果尝试为一个const变量赋值,编译时将会报错。
const关键字定义的常量,遵守 variable scoping 管辖的其他变量的规则。这一点加上使用 #define的缺陷 ,使 const 关键字成为定义常量的一个的首选方法。
例子
const float pi = 3.14;
float x;
// ....
x = pi * 2; // 在数学表达式中使用常量不会报错
pi = 7; // 错误的用法 - 你不能修改常量值,或给常量赋值。
您可以使用 const 或 #define 创建数字或字符串常量。但 arrays, 你只能使用 const。 一般 const 相对 的#define是首选 的定义常量语法。
辅助工具
sizeof()
描述
sizeof操作符返回一个变量类型的字节数,或者该数在数组中占有的字节数。
语法
sizeof(variable)
参数
variable: 任何变量类型或数组(如int,float,byte)
示例代码
sizeof操作符用来处理数组非常有效,它能很方便的改变数组的大小而不用破坏程序的其他部分。
这个程序一次打印出一个字符串文本的字符。尝试改变一下字符串。
char myStr[] = "this is a test";
int i;
void setup(){
Serial.begin(9600);
}
{0}void{/0}{1} {/1}{2}loop{/2}{1}() {{/1}
for (i = 0; i < sizeof(myStr) - 1; i++){
Serial.print(i, DEC);
Serial.print(" = ");
Serial.println(myStr[i], BYTE);
}
}
请注意sizeof返回字节数总数。因此,较大的变量类型,如整数,for循环看起来应该像这样。
for (i = 0; i < (sizeof(myInts)/sizeof(int)) - 1; i++) {
//用myInts[i]来做些事
}