在Arduino中程序运行时将首先调用 setup() 函数。用于初始化变量、设置针脚的输出\输入类型、配置串口、引入类库文件等等。每次 Arduino板子 上电或重启后,setup 函数只运行一次。
int buttonPin = 3;
void setup()
{
Serial.begin(9600);
pinMode(buttonPin, INPUT);
}
void loop()
{
// ...
}
在 setup() 函数中初始化和定义了变量,然后执行 loop() 函数。顾名思义,该函数在程序运行过程中不断的循环,根据一些反馈,相应改变执行情况。通过该函数动态控制 Arduino 主控板。
int buttonPin = 3; // setup 中初始化串口和按键针脚.
void setup()
{
beginSerial(9600);
pinMode(buttonPin, INPUT);
}
// loop 中每次都检查按钮,如果按钮被按下,就发送信息到串口
void loop()
{
if (digitalRead(buttonPin) == HIGH)
serialWrite('H');
else
serialWrite('L');
delay(1000);
}
结构控制这一块和C语言类似,就是控制程序执行流程的分支、循环、顺序结构的一些特定语法。
熟悉C语言的话,这块不用过一眼就好。
if(条件判断语句)和 ==、!=、<、>(比较运算符)
if 语句与比较运算符一起用于检测某个条件是否达成,如某输入值是否在特定值之上等。if 语句的语法是:
if (someVariable > 50)
{
// 执行某些语句
}
本程序测试 someVariable 变量的值是否大于 50。当大于 50 时,执行一些语句。换句话说,只要 if 后面括号里的结果(称之为测试表达式)为真,则执行大括号中的语句(称之为执行语句块);若为假,则跳过大括号中的语句。 if 语句后的大括号可以省略。若省略大括号,则只有一条语句(以分号结尾)成为执行语句。
if (x > 120) digitalWrite(LEDpin, HIGH);
if (x > 120)
digitalWrite(LEDpin, HIGH);
if (x > 120){ digitalWrite(LEDpin, HIGH); }
if (x > 120){
digitalWrite(LEDpin1, HIGH);
digitalWrite(LEDpin2, HIGH);
} // 以上所有书写方式都正确
在小括号里求值的表达式,需要以下操作符:
比较运算操作符:
x == y(x 等于 y)
x != y(x 不等于 y)
x < y(x 小于 y)
x > y(x 大于 y)
x <= y(x 小于等于 y)
x >= y(x 大于等于 y)
警告:
注意使用赋值运算符的情况(如 if (x = 10))。一个“=”表示的是赋值运算符,作用是将 x 的值设为 10(将值 10 放入 x 变量的内存中)。两个“=”表示的是比较运算符(如 if (x == 10)),用于测试 x 和 10 是否相等。后面这个语句只有 x 是 10 时才为真,而前面赋值的那个语句则永远为真。
这是因为 C 语言按以下规则进行运算 if (x=10):10 赋值给 x(只要非 0 的数赋值的语句,其赋值表达式的值永远为真),因此 x 现在值为 10。此时 if 的测试表达式值为 10,该值永远为真,因为非 0 值永远为真。所以,if (x = 10) 将永远为真,这就不是我们运行 if 所期待的结果。另外,x 被赋值为 10,这也不是我们所期待的结果。
if 的另外一种分支条件控制结构是 if…else 形式。
if/else是比if更为高级的流程控制语句,它可以进行多次条件测试。比如,检测模拟输入的值,当它小于500时该执行哪些操作,大于或等于500时执行另外的操作。代码如下:
if (pinFiveInput < 500)
{
// 执行A操作
}
else
{
// 执行B操作
}
else可以进行额外的if检测,所以多个互斥的条件可以同时进行检测。
测试将一个一个进行下去,直到某个测试结果为真,此时该测试相关的执行语句块将被运行,然后程序就跳过剩下的检测,直接执行到if/else的下一条语句。当所有检测都为假时,若存在else语句块,将执行默认的else语句块。
注意else if语句块可以没有else语句块。else if分支语句的数量无限制。
if (pinFiveInput < 500)
{
// 执行A操作
}
else if (pinFiveInput >= 1000)
{
// 执行B操作
}
else
{
// 执行C操作
}
另外一种进行多种条件分支判断的语句是switch case语句。
switch / case语句
和if语句相同,switch…case通过程序员设定的在不同条件下执行的代码控制程序的流程。特别地,switch语句将变量值和case语句中设定的值进行比较。当一个case语句中的设定值与变量值相同时,这条case语句将被执行。
关键字break可用于退出switch语句,通常每条case语句都以break结尾。如果没有break语句,switch语句将会一直执行接下来的语句(一直向下)直到遇见一个break,或者直到switch语句结尾。
例子
switch (var) {
case 1:
//当var等于1时,执行一些语句
break;
case 2
//当var等于2时,执行一些语句
break;
default:
//如果没有任何匹配,执行default
//default可有可不有
}
语法
switch (var) {
case label:
// 声明
break;
case label:
// 声明
break;
default:
// 声明
}
参数
var: 用于与下面的case中的标签进行比较的变量值
label: 与变量进行比较的值
for语句
描述
for语句用于重复执行一段在花括号之内的代码。通常使用一个增量计数器计数并终止循环。for语句用于重复性的操作非常有效,通常与数组结合起来使用来操作数据、引脚。
for循环开头有3个部分:
(初始化;条件;增量计数){
//语句
}
“初始化”只在循环开始执行一次。每次循环,都会检测一次条件;如果条件为真,则执行语句和“增量计数”,之后再检测条件。当条件为假时,循环终止。
例子
//用PWM引脚将LED变暗
int PWMpin = 10; //将一个LED与47Ω电阻串联接在10脚
void setup()
{
//无需设置
}
void loop()
{
for (int i=0; i <= 255; i++)
{
analogWrite(PWMpin, i);
delay(10);
}
}
编程提示
C语言的for循环语句比BASIC和其他电脑编程语言的for语句更灵活。除了分号以外,其他3个元素都能省略。同时,初始化,条件,增量计算可以是任何包括无关变量的有效C语句,任何C数据类型包括float。这些不寻常的for语句可能会解决一些困难的编程问题。
例如,在增量计数中使用乘法可以得到一个等比数列:
for(int x = 2; x < 100; x = x * 1.5){
println(x);
}
生成:2,3,4,6,9,13,19,28,42,63,94
另一个例子,使用for循环使LED产生渐亮渐灭的效果:
void loop()
{
int x = 1;
for (int i = 0; i > -1; i = i + x)
{
analogWrite(PWMpin, i);
if (i == 255) x = -1; // 在峰值转变方向
delay(10);
}
}
while循环
描述
while循环会无限的循环,直到括号内的判断语句变为假。必须要有能改变判断语句的东西,要不然while循环将永远不会结束。这在您的代码表现为一个递增的变量,或一个外部条件,如传感器的返回值。
语法
while(表达){
//语句
}
参数
表达:为真或为假的一个计算结果
例子
var = 0;
while(var < 200){
//重复一件事200遍
var++
}
do…while循环与while循环运行的方式是相近的,不过它的条件判断是在每个循环的最后,所以这个语句至少会被运行一次,然后才被结束。
do
{
//语句
}while(测试条件);
例子
do{
delay(50); //等待传感器稳定
X = readSensors(); //检查传感器取值
}while(X <100); //当x小于100时,继续运行
break用于退出do,for,while 循环,能绕过一般的判断条件。它也能够用于退出 switch语句。
例子
for (x = 0; x < 255; x ++)
{
digitalWrite(PWMpin, x);
sens = analogRead(sensorPin);
if (sens > threshold){ // 超出探测范围
x = 0;
break;
}
delay(50);
}
continue语句跳过当前循环中剩余的迭代部分( do,for 或 while )。它通过检查循环条件表达式,并继续进行任何后续迭代。
例子
for (x = 0; x < 255; x ++)
{
if (x > 40 && x < 120){ // 当x在40与120之间时,跳过后面两句,即迭代。
continue;
}
digitalWrite(PWMpin, x);
delay(50);
}
终止一个函数,如有返回值,将从此函数返回给调用函数。
语法
return;
return value; // 两种形式均可
参数
value:任何变量或常量的类型
例子
一个比较传感器输入阈值的函数
int checkSensor(){
if (analogRead(0) > 400) {
return 1;}
else{
return 0;
}
}
return关键字可以很方便的测试一段代码,而无需“comment out(注释掉)” 大段的可能存在bug的代码。
void loop(){
//写入漂亮的代码来测试这里。
return;
//剩下的功能异常的程序
//return后的代码永远不会被执行
}
程序将会从程序中已有的标记点开始运行
语法
label:
goto label; //从label处开始运行
提示
不要在C语言中使用goto编程,某些C编程作者认为goto语句永远是不必要的,但用得好,它可以简化某些特定的程序。许多程序员不同意使用goto的原因是, 通过毫无节制地使用goto语句,很容易创建一个程序,这种程序拥有不确定的运行流程,因而无法进行调试。
的确在有的实例中goto语句可以派上用场,并简化代码。例如在一定的条件用if语句来跳出高度嵌入的for循环。
例子
for(byte r = 0; r < 255; r++){
for(byte g = 255; g > -1; g--){
for(byte b = 0; b < 255; b++){
if (analogRead(0) > 250){
goto bailout;
}
//更多的语句...
}
}
}
bailout:
用于表示一句代码的结束。
例子
int a = 13;
提示
在每一行忘记使用分号作为结尾,将导致一个编译错误。错误提示可能会清晰的指向缺少分号的那行,也可能不会。如果弹出一个令人费解或看似不合逻辑的编译器错误,第一件事就是在错误附近检查是否缺少分号。
大括号(也称为“括号”或“大括号”)是C编程语言中的一个重要组成部分。它们被用来区分几个不同的结构,下面列出的,有时可能使初学者混乱。
左大括号“{”必须与一个右大括号“}”形成闭合。这是一个常常被称为括号平衡的条件。在Arduino IDE(集成开发环境)中有一个方便的功能来检查大括号是否平衡。只需选择一个括号,甚至单击紧接括号的插入点,就能知道这个括号的“伴侣括号”。
目前此功能稍微有些错误,因为IDE会经常会认为在注释中的括号是不正确的。
由于大括号被用在不同的地方,这有一种很好的编程习惯以避免错误:输入一个大括号后,同时也输入另一个大括号以达到平衡。然后在你的括号之间输入回车,然后再插入语句。这样一来,你的括号就不会变得不平衡了。不平衡的括号常可导致许多错误,比如令人费解的编译器错误,有时很难在一个程序找到这个错误。由于其不同的用法,括号也是一个程序中非常重要的语法,如果括号发生错误,往往会极大地影响了程序的意义。
大括号中的主要用途
功能
void myfunction(datatype argument){
statements(s)
}
循环
while (boolean expression)
{
statement(s)
}
do
{
statement(s)
} while (boolean expression);
for (initialisation; termination condition; incrementing expr)
{
statement(s)
}
条件语句
if (boolean expression)
{
statement(s)
}
else if (boolean expression)
{
statement(s)
}
else
{
statement(s)
}
Comments(注释)
注释用于提醒自己或他人程序是如何工作的。它们会被编译器忽略掉,也不会传送给处理器,所以它们在Atmega芯片上不占用体积。 注释的唯一作用就是使你自己理解或帮你回忆你的程序是怎么工作的或提醒他人你的程序是如何工作的。编写注释有两种写法:
例子
x = 5; // 这是一条注释斜杠后面本行内的所有东西是注释
/* 这是多行注释-用于注释一段代码
if (gwb == 0){ // 在多行注释内可使用单行注释
x = 3; /* 但不允许使用新的多行注释-这是无效的
}
// 别忘了注释的结尾符号-它们是成对出现的!
*/
小提示
当测试代码的时候,注释掉一段可能有问题的代码是非常有效的方法。这能使这段代码成为注释而保留在程序中,而编译器能忽略它们。这个方法用于寻找问题代码或当编译器提示出错或错误很隐蔽时很有效。
#define 是一个很有用的C语法,它允许程序员在程序编译之前给常量命名。在Arduino中,定义的常量不会占用芯片上的任何程序内存空间。在编译时编译器会用事先定义的值来取代这些常量。
然而这样做会产生一些副作用,例如,一个已被定义的常量名已经包含在了其他常量名或者变量名中。在这种情况下,文本将被#defined 定义的数字或文本所取代。
通常情况下,优先考虑使用 const 关键字替代 #define 来定义常量。
Arduino 拥有和 C 相同的语法规范。
语法
#define 常量名 常量值 注意,#是必须的。
例子
#define ledPin 3
//在编译时,编译器将使用数值 3 取代任何用到 ledPin 的地方。
提示
在#define 声明后不能有分号。如果存在分号,编译器会抛出语义不明的错误,甚至关闭页面。
#define ledPin 3; //这是一种错误写法
类似的,在#define声明中包含等号也会产生语义不明的编译错误从而导致关闭页面。
#define ledPin = 3 //这是一种错误写法
#include用于调用程序以外的库。这使得程序能够访问大量标准C库,也能访问用于arduino的库。 AVR C库(Arduino基于AVR标准语法)语法手册请点击这里。 注意#include和#define一样,不能在结尾加分号,如果你加了分号编译器将会报错。
例子
此例包含了一个库,用于将数据存放在flash空间内而不是ram内。这为动态内存节约了空间,大型表格查表更容易实现。
#include
prog_uint16_t myConstants[] PROGMEM = {0, 21140, 702 , 9128, 0, 25764, 8456,0,0,0,0,0,0,0,0,29810,8968,29762,29762,4500};