arduino语言基础
arduino是用什么语言写的?
Arduino使用的编程语言主要是基于C++语言的一种简化版本,称为Arduino语言或Wiring语言。Arduino语言在C++的基础上进行了一些简化和封装,使得用户可以更加轻松地进行硬件编程。Arduino语言的编程方式类似于C++,包括变量、控制结构、函数等基本语法,同时还有许多库函数可以方便用户进行各种操作,如控制GPIO口、读取传感器数据、进行串口通讯等。Arduino语言的代码可以在Arduino IDE中编写和调试,然后上传到Arduino板上运行。
1、arduino程序框架
/* 1、此程序通过按键开关来控制LED灯的亮灭,当按键按下时LED灯点亮,松开是LED灯熄灭; 2、LED接13引脚 3、按键开关接4引脚 */ #include <stdio.h> #define LED_PIN 13 #define BUTTON_PIN 4 float temp = 0; bool button_state = false; int power = 0; void setup() { // put your setup code here, to run once: pinMode(LED_pin,OUTPUT); pinMode(BUTTON_pin,INPUT); Serial.begin(9600); } void loop() { // put your main code here, to run repeatedly: button_satte = digitalRead(BUTTON_pin); Serial.println(button_satte); if (button_satte) { digitalWrite(LED_pin,HIGH); } }
2、基本语法
2.1 注释符
//
/ *
*/
2.2 语句结束符
;
2.3 代码块分界符
{ }
2.4 宏定义符
#define
2.5 头文件引用符
#include
3、数据类型
3.1 基本数据类型
数据类型 | 日常写法 | C/C++中写法 | C/C++中类型标识符 | 数值范围 |
---|---|---|---|---|
整数 | 1,100, | 1,100 | int | -32768~32767 |
小数 | 3.14,2.7345 | 3.14,2.7345 | float | -3.4028235E+38~3.4028235E+38 |
字符 | a,b,我 | 'a','b','我' | char | -128~127 |
文本 | 遇见你真高兴! | “遇见你真高兴!” | string | |
逻辑判断 | 对、错 | true,false | bool | true,false |
3.2 基本数据类型转换函数
char(value)
int(value)
float(value)
String(value)
itoa(int value,char*string,int radix)
atoi(const char *nptr)
3.3 整型数不同进制表示
整数常量默认为十进制,但在前面加上特殊前缀表示为其他进制数。
进制 | 例子 | 格式 | 备注 |
---|---|---|---|
十进制 | 123 | 无 | 无 |
二进制 | 0b1111011 | 前缀0b或0B | 只适用于8位的值(0到255)字符0-1有效 |
八进制 | 0173 | 前缀0 | 字符0-7有效 |
十六进制 | 0x7B | 前缀0x或0X | 字符0-9,A-F,A-F有效 |
3.4 数组 array
数组是相同类型的数据组成的集合,数组中的每个元素都被默认分配一个索引(下标),我们可以通过数组名[ 索引 ]的方式访问数组中的元素。
创建数组
语法格式:
datatype arrayname[ 数组元素个数];
int Ints[6]; int P[] = {2, 4, 8, 3, 6}; int SensVals[6] = {2, 4, -8, 3, 2}; char message[6] = "hello";
datatype:声明数组中存放元素的数据类型;
arrayname:数组名称,像给变量命名一样给数组起一个名字;
数组初始化
我们可以在定义数组的同时进行初始化,即给数组赋值:
int a[4] = {20, 345, 700, 22};
{ }中的值即为各元素的初值,各值之间用,(逗号)隔开。
数组赋初值需要注意以下几点:
1) 可以只给部分元素赋初值。当{ }中值的个数少于元素个数时,只给前面部分元素赋值。例如:
int a[10]={12, 19, 22 , 993, 344};
表示只给 a[0] ~ a[4] 这5个元素赋值,而后面5个元素自动赋0值。
当赋值的元素少于数组总体元素的时候,剩余的元素自动初始化为 0:对于int类型数据,就是整数0;对于char类型数据,就是字符 ‘\0’(空字符);对于float类型数据,就是小数0.0。
我们可以通过下面的形式将数组的所有元素初始化为 0:
int a[10] = {0}; char c[10] = {0}; float f[10] = {0};
由于剩余的元素会自动初始化为0,所以只需要给第0个元素赋0值即可。
使用数组
数组中的每个元素都有一个序号,这个序号从0开始,而不是从我们熟悉的1开始,称为下标(Index)。使用数组元素时,指明下标即可,形式为:
arrayName[index]
arrayName 为数组名称,index 为下标。例如,a[0] 表示第0个元素,a[3] 表示第3个元素
实例:
void setup() { Serial.begin(9600); int a[6] = {299, 34, 92, 100}; // 定义数组 for(int i=0; i<6; i++){ //串口监视器输出数组元素 Serial.print("a["); Serial.print(i); Serial.print("] ="); Serial.println(a[i]); Serial.println(""); } } void loop() { while(1){continue;} }
我们还可以根据需要定义二维、三维等多维数组,此处忽略。
4、变量
4.1 什么是变量
数学课中我们是不是这样写 长 = 10,宽 = 5,
在程序中我们也经常需要用一个名称来代表某个数据,
如 length = 10,width = 5, 这里的length、width就是变量,我们把数据放到变量中,后面要用到这个数据时我们直接用变量名就可以了。
4.2 变量的作用
变量是用来存储程序运行过程中用到的数据,使用变量可以增加程序的简洁性、易读性和易维护性。
4.3 变量的定义和应用
定义变量语法格式
变量是用来存储数据的,数据有整型、小数、文本等多种类型,因此在C/C++中定义变量必须先声明变量类型,变量定义语法如下:
int length,width;
float square;
char ch;
string name ="arduino"
c/c++中变量的值可以变化,但变量的类型不能变化。
变量的应用
定义完变量,我们在代码中就可以对变量进行赋值、引用
length =10;
width = 5;
square = length * width
printf(square)
变量命名规则:
变量名可以是字母、数字和下划线的组合,但必须遵守以下规则:
1、变量名必须以字母或下划线开头,不能是数字;
2、变量名中的字母是区分大小写的。比如 a 和 A 是不同的变量名,num 和 Num 也是不同的变量名。
3、变量名不能是关键字;
变量命名规范建议:
1、见名知义;
2、小驼峰格式;
3、下划线分段格式;
5、数据运算符
5.1 算术运算符
加 +
减 -
乘 *
除 / 注意:被除数和除数都为整数,结果只保留整数,舍去小数部分,想得到小数需把被除数或除数任意一个转换为小数
取余 %
自增 ++
自减 --
5.2 赋值符
=
复合赋值运算符
+=
-=
*=
/=
%=
5.3 关系运算符
等于 ==
不等于 !=
大于 >
小于 <
大于等于 >=
小于等于 <=
5.4 逻辑运算符(布尔运算符)
逻辑与 &&
逻辑或 ||
逻辑非 !
5.5 位运算符
位与 &
位或 |
位异或 ^
位非 ~
左移 <<
右移 >>
6、程序三大流程控制
6.1 顺序结构
6.2 分支结构(选择结构)
if 语句 (单分支结构)
通过 if 语句,可以让程序判断某一个条件是否达到,并且根据判断结果执行相应的程序。
if语句语法格式:
if(判断条件) { 语句块 }
代码执行逻辑:
如果 “判断条件” 为真则执行语句块,否则将不执行该语句块,if语句流程图:
if ...else 语句 (双分支结构)
语法格式:
if( 判断条件 ) { 语句块1 } else { 语句块2 }
代码执行逻辑:
如果 “判断条件” 为真则执行”语句块1″。为假将执行”语句块2″,if...else语句流程图:
实例:
void setup() { Serial.begin(9600); //开始串口通讯 pinMode(2, INPUT_PULLUP); //将引脚2设置为输入上拉模式 pinMode(13, OUTPUT); } void loop() { int sensorVal = digitalRead(2); //将开关状态数值读取到变量中 Serial.println(sensorVal); //输出开关状态数值 //按钮被按下后,引脚13连接的LED将被点亮。按钮没有按下时,LED熄灭。 //如果按钮没有按下,熄灭LED。否则,点亮LED if (sensorVal == HIGH) { //按钮没有按下 digitalWrite(13, LOW); //熄灭LED } else { //否则 digitalWrite(13, HIGH); //点亮LED } }
多个if ...else 语句 (多分支结构)
语法格式:
if(判断条件1){ 语句块1 } else if(判断条件2){ 语句块2 }else if(判断条件3){ 语句块3 }else if(判断条件m){ 语句块m }else{ 语句块n }
代码执行逻辑:
从上到下依次检测判断条件,当某个判断条件成立时,则执行其对应的语句块。如果所有判断条件都不成立,则执行语句块n。
switch case语句
switch语句通过对一个变量的值与case语句中指定的值进行比较。当一个case语句中的指定值与switch语句中的变量相匹配,就会运行这个case语句下的代码。通过switch case语句,实现多分支功能。
语法格式:
switch (var) { case 1: //当var等于1时执行这里的程序 代码块1 break; case 2: 代码块2 //当var等于2时执行这里的程序 break; default: // 如果var的值与以上case中的值都不匹配 // 则执行这里的程序 代码块3 break; }
switch case语句使用注意事项:
1) 当变量var和某个case后面的数值匹配成功后,如果没有break, 程序会执行该分支以及后面所有分支的语句。
2) case 后面必须是一个整数,或者是结果为整数的表达式,但不能包含任何变量。
3) case 后面不能使用字符串,但可以使用字符,使用字符时需要用单引号把字符括起来,如: case: 'b'。
4) default 不是必须的。当没有 default 时,如果所有 case 都匹配失败,那么就什么都不执行。
实例
void setup() { Serial.begin(9600); // 初始化串口通讯 for (int thisPin = 2; thisPin < 7; thisPin++) {// 初始化Arduino连接LED的引脚 pinMode(thisPin, OUTPUT); } } void loop() { if (Serial.available() > 0) { int inByte = Serial.read(); // Arduino用switch语句,根据接收到的不同信息进行相应的反应。 switch (inByte) { case 'a': digitalWrite(2, HIGH); break; case 'b': digitalWrite(3, HIGH); break; case 'c': digitalWrite(4, HIGH); break; case 'd': digitalWrite(5, HIGH); break; case 'e': digitalWrite(6, HIGH); break; default: // 熄灭所有LED: for (int thisPin = 2; thisPin < 7; thisPin++) { digitalWrite(thisPin, LOW); } break; } } }
6.3 循环结构
while 循环语句
语法格式:
while(表达式){ 循环体 }
while循环执行逻辑:先计算表达式的值,当值为真(非0)时, 执行循环体语句;执行完循环体语句,再次计算表达式的值,如果为真,继续执行循环体……这个过程会一直重复,直到表达式的值为假(0)才退出循环。流程图:
实例:
void setup() { // 初始化串口通讯 Serial.begin(9600); } void loop() { int i=1, sum=0; while(i<=100){ //判断i是否小于等于零 sum+=i; //当i小于等于零时, i++; //执行循环体中的语句。 } Serial.print ("sum = "); //通过串口监视器输出 Serial.println (sum); //while循环结束后的sum值 delay (5000); // 延迟5秒钟 }
do while循环语句
语法格式:
do{ 代码块语句 } while(表达式);
程序执行逻辑:先执行循环体代码块语句,然后再判断表达式是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while 循环至少要执行一次循环体。
for 循环
语法格式:
for(表达式1; 表达式2; 表达式3){ 语句块 }
for循环的执行过程如下:
1) 先执行表达式1。
2) 再执行表达式2,若其值为真(非0),则执行括号中的语句块,否则将结束循环。
3) 执行完循环体中的语句块,再执行表达式3。
4) 重复执行步骤 2) 和 3),直到“表达式2”的值为假,就结束循环。
注意:表达式1仅在第一次循环时执行,以后都不会再执行,可以认为这是一个初始化语句。
void setup(){ int i, sum=0; for(i=1; i<=100; i++){ sum = sum + i; } } void loop(){ }
使用for语句应该注意
1) for循环中的“表达式1(循环变量赋初值)”、“表达式2(循环条件)”和“表达式3(循环变量增量)”都是选择项,即可以缺省,但分号(;)不能缺省。
2) 省略了“表达式1(循环变量赋初值)”,表示不对循环控制变量赋初值。
3) 省略了“表达式2(循环条件)”,如果不做其它处理就会成为死循环。
4) 省略了“表达式3(循环变量增量)”,则不对循环控制变量进行操作,这时可在语句体中加入修改循环控制变量的语句。如以下示例:
for( i=1; i<=100; ){ sum=sum+i; i++; }
c语言中,for 循环使用更加灵活,完全可以取代 while 循环
实例:
int PWMpin = 9; //引脚9通过限流电阻连接LED void setup() { } void loop() { for (int i=0; i <= 255; i++){ //开始运行for循环语句 analogWrite(PWMpin, i); //对引脚9写入i的数值 delay(10); //延迟10毫秒 } }
break语句
break语句用于绕过正常循环条件并中止do,for,或while循环,也可用于中止switch语句。
例如:
void setup() { // 初始化串口通讯 Serial.begin(9600); } void loop() { int i; int sum = 0; while(1){ //循环条件为死循环 sum+=i; i++; if(i>100){ break; } } Serial.print ("sum = "); //通过串口监视器输出 Serial.println (sum); //do-while循环结束后的sum值 delay (5000); // 延迟5秒钟 }
continue语句
continue语句的作用是跳过循环体中剩余的语句而强制进入下一次循环。continue语句用于 while、for 循环中,常与 if 条件语句一起使用,判断条件是否成立。
void setup() { pinMode (3, OUTPUT); } void loop() { for (int x = 0; x < 255; x ++) { if (x > 40 && x < 120){ // 当x大于40或小于120 continue; // 跳过此次循环 } analogWrite(3, x); } }
return
7、函数
7.1 什么是函数
封装在一起,实现一定功能的代码就是函数。函数的使用可以使程序模块化,增加代码的复用度。
void delay(int ms) { int start = micros(); while (ms > 0) { yield(); while ( ms > 0 && (micros() - start) >= 1000) { ms--; start += 1000; } } }
7.2 函数的定义
dataType functionName( dataType1 param1, dataType2 param2 ... ){ //body }
- dataType 是返回值类型,它可以是C语言中的任意数据类型,例如 int、float、char 等。
- functionName 是函数名,命名规则和变量命名规则相同。函数名后面的括号( )不能少。
- body 是函数体,它是函数需要执行的代码,是函数的主体部分。函数体要用{ }包围。
- 如果有返回值,在函数体中使用 return 语句返回。return 出来的数据的类型要和 dataType 一样。
- dataType1 param1, dataType2 param2 ...是参数列表。函数可以没有参数,也可以有一个或多个参数,多个参数之间由,分隔。参数本质上也是变量,定义时要指明类型和名称。
形式参数概念:
在定义函数时的参数变量没有具体的数据,它只能等到函数被调用时接收传递进来的数据,所以函数定义时的参数称为形式参数,简称形参。
例如:定义一个计算m到n之间所有整数的和,并返回结果
int sum(int m, int n){ int i, sum=0; for(i=m; i<=n; i++){ sum+=i; } return sum; }
return语句
终止一个函数,并向调用此函数的函数返回一个值
7.3 函数的调用
functionName(param1, param2, param3 ...);
functionName 是函数名称,param1, param2, param3 ...是实参列表。实参的个数和类型要和函数定义时的参数个数和类型一致。
实际参数概念:
函数被调用时给出的参数被赋予了具体的数据,所以函数调用时的参数称为实际参数,简称实参。函数调用时,实参的值会传递给形参。
int i = 10,j = 1000; void setup() { Serial.begin(9600); } void loop() { int sum1 = sum(1,100); Serial.Println(sum); Serial.Println(sum(i,j)); }
7.4 变量的作用域
所谓作用域(Scope),就是变量的有效使用范围。变量都有自己的作用域。决定变量作用域的是变量的定义位置。
局部变量
定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部, 离开该函数后就是无效的,再使用就会报错。
全局变量
在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序。
8、arduino内置常用函数
8.1 数字 I/O函数
pinMode(pin_num,模式) //设置引脚的输入输出模式 digitalWrite(pin_num,value) //对数字引脚进行高低电平设置 digitalRead(pin_num) //读取数字引脚状态
8.2 模拟 I/O函数
analogRead(pin) //从模拟引脚读取数值,范围0到1023 analogWrite(pin,value) //向模拟引脚写入数值,范围0到255,通过PWM占空比可以控制 //LED灯亮度、电机的转速等
8.3 时间函数
millis()//用来获取Arduino开机后运行的时间长度,单位是毫秒, //最长可记录接近50天左右的时间。如果超出记录时间上限,记录将从0重新开始。 micros()//用来获取Arduino开机后运行的时间长度,单位是微秒, //最长可记录接近70天左右的时间。如果超出记录时间上限,记录将从0重新开始。 delay(x)//用于暂停程序运行。暂停时间可以由delay()函数的参数进行控制, //单位是毫秒(1秒钟=1000毫秒)。 delayMicroseconds(x)//与delay()函数功能一样,不同的是delayMicroseconds()的 //参数单位是微秒
8.4 产生随机数函数
random()函数 //生成并返回一个随机数
long randNumber; void setup(){ Serial.begin(9600); } void loop(){ randNumber = random(0, 300); //产生0-300间的随机数 Serial.println(randNumber); delay(50); }
randomSeed()函数 //用来产生随机种子
单独使用random()函数所产生的随机数,在每一次程序重新启动后,总是重复同一组随机数字。如果希望程序重新启动后产生的随机数值与上一次程序运行时的随机数不同,则需要使用randomSeed()函数。
在实际应用时,可以通过调用analogRead()函数读取一个空引脚,作为随机种子数值,或用micros()得到一个时间作为随机数种子。
long randNumber; void setup(){ Serial.begin(9600); randomSeed(analogRead(A0)); //将引脚A0放空,每次程序启动时所读取的数值都是不同的。 //这么做可以产生真正的随机种子值,从而产生随机数值。 } void loop(){ randNumber = random(300); // 产生随机数 Serial.println(randNumber); delay(50); }
8.5 数学运算函数
min(x,y) //求两个数之间的最小值 max(x,y)//求两个数之间的最大值 abs(x) //求绝对值 pow(base, exponent) //指数运算函数,返回base数值的exponent次方 sqrt(value) //开方函数,返回value数值的平方根 sin(rad) //计算一个角度的正玄值并返回,rad参数单位为弧度,需要把度数转换为弧度 cos(rad) //计算一个角度的余弦值并返回,rad参数单位为弧度,需要把度数转换为弧度 tan(rad) //计算一个角度的正玄值并返回,rad参数单位为弧度,需要把度数转换为弧度
8.6 串口通讯函数
Serial.begin() //设置电脑与Arduino进行串口通讯时的数据传输速率(每秒传输bit数) Serial.setTimeout(time)//用于设置设备等待数据超时时间。单位毫秒,默认为1000毫秒 Serial.print(value) //以字符形式向串口发送数据 Serial.println(value)//以字符形式向串口发送数据,并换行 Serial.available()//用于检查串口是否接收到数据。返回等待读取的数据字节数。 Serial.parseInt()//用于从设备接收到的数据中寻找整数数值并返回。 Serial.read() //从设备接收到数据中读取一个字节的数据。 Serial.readBytes(buffer, length) //用于从设备接收的数据中读取信息。读取到的数据信息将存放在缓存变量中。 //该函数在读取到指定字节数的信息或者达到设定时间后都会停止函数执行并返回。 Serial.readBytesUntil(character, buffer, length) //用于从设备接收到数据中读取信息。读取到的数据信息将存放在缓存变量中。 //该函数在满足以下任一条件后都会停止函数执行并且返回。 //– 读取到指定终止字符 //– 读取到指定字节数的信息 //– 达到设定时间(可使用setTimeout来设置) Serial.readString()//用于从设备接收到数据中读取数据信息。 //读取到的信息将以字符串格式返回。 Serial.readStringUntil(terminator) //用于从设备接收到的数据中读取信息。读取到的数据信息将以字符串形式返回。 //该函数在满足以下任一条件后都会停止函数执行并返回。 //– 读取到指定终止字符 //– 达到设定时间(可使用setTimeout来设置) Serial.write(val) Serial.write(str) Serial.write(buf, len) //以二进制数据向串口发送数据,数据是一个字节一个字节地发送的, //若以字符形式发送数字请使用print()代替。 参数 //val: 作为单个字节发送的数据 //str: 由一系列字节组成的字符串 //buf: 同一系列字节组成的数组 //len: 要发送的数组的长度
实例:
Serial.begin()、Serial.println()、Serial.parseInt()函数用法: void setup() { // 启动串口通讯 Serial.begin(9600); Serial.println(); } void loop() { if (Serial.available()){ // 当串口接收到信息后 int serialData = Serial.parseInt(); // 使用parseInt查找接收到的信息中的整数 Serial.print("serialData = "); // 然后通过串口监视器输出找到的数值 Serial.println(serialData); } } Serial.read()用法: void setup() { // 启动串口通讯 Serial.begin(9600); Serial.println(); } void loop() { while (Serial.available()){ // 当串口接收到信息后 char serialData = Serial.read(); // 将接收到的信息使用read读取 Serial.println((char)serialData); // 然后通过串口监视器输出read函数读取的信息 } } //char terminateChar = 'T'; // 建立终止字符 const int bufferLength = 10; // 定义缓存大小为10个字节 char serialBuffer[bufferLength];// 建立字符数组用于缓存 void setup() { // 启动串口通讯 Serial.begin(9600); Serial.println(); } void loop() { if (Serial.available()){ // 当串口接收到信息后 Serial.println("Received Serial Data:"); // 将接收到的信息使用readBytesUntil读取 //Serial.readBytesUntil(terminateChar, serialBuffer, bufferLength); //将接收到的信息使用readBytes读取 Serial.readBytes(serialBuffer, bufferLength); for(int i=0; i<bufferLength; i++){ // 然后通过串口监视器输出readBytes Serial.print(serialBuffer[i]); // 函数所读取的信息 } Serial.println(""); Serial.println("Finished Printing Recevied Data."); } }