第2章 Arduino的基本函数
从第1章中我们已经初步了解了Arduino的易用性、自由性与交互性,然而它真正吸引众多爱好者是因为提供了大量的基础函数,包括I/O控制、时间函数、中断函数、数学函数、串口通信函数等,这些基础函数使单片机系统开发不再有复杂的底层代码,没有难懂的汇编语言,使用者可以方便地对板上的资源进行控制。另外,Arduino还提供了许多关于这些基础函数的示例程序,这些示例可以在Arduino开发环境的“File→Examples”菜单中找到,从而大大地降低了初学者的学习难度,缩短了单片机系统开发周期。
2.1 数字I/O口的操作函数
2.1.1 pinMode(pin, mode)
pinMode函数用于配置引脚为输入或输出模式,它是一个无返回值函数,一般放在setup里,先设置再使用。
pinMode函数有两个参数——pin和mode。pin参数表示要配置的引脚,以Arduino Uno为例,它的范围是数字引脚0~13,也可以把模拟引脚(A0~A5)作为数字引脚使用,此时编号为14脚(对应模拟引脚0)到19脚(对应模拟引脚5)。mode参数表示设置的模式——INPUT(输入)或OUTPUT(输出),其中INPUT用于读取信号,OUTPUT用于输出控制信号。
配置数字引脚3为输出模式的语句如下:
pinMode(3, OUTPUT);
2.1.2 digitalWrite(pin,value)
digitalWrite函数的作用是设置引脚的输出电压为高电平或低电平,也是一个无返回值函数,在使用该函数设置引脚之前,需要先用pinMode将引脚设置为OUTPUT模式。
digitalWrite函数有两个参数——pin和value,pin参数表示所要设置的引脚,value参数表示输出的电压——HIGH(高电平)或LOW(低电平)。
配置数字引脚2的输出电平为高电平的语句如下:
pinMode(2, OUTPUT); digitalWrite(2, HIGH);
2.1.3 digitalRead(pin)
digitalRead函数的作用是获取引脚的电压情况,该函数返回值为int型——HIGH(高电平)或者LOW(低电平),在使用该函数设置引脚之前,需要先用pinMode将引脚设置为INPUT模式。
digitalRead函数只有一个参数——pin,它表示所要获取电压情况的引脚号,如果引脚没有连接到任何地方,那么将随机返回HIGH(高电平)或者LOW(低电平)。
获取数字引脚4的电压情况的语句如下:
pinMode(4, INPUT); digitalRead(4);
2.2 模拟I/O口的操作函数
2.2.1 analogReference(type)
analogReference函数的作用是配置模拟输入引脚的基准电压(即输入范围的最大值),它是一个无返回值函数,只有一个参数type,type的选项有DEFAULT/INTERNAL/INTERNAL1V1/INTERNAL2V56/EXTERNAL,其具体含义如下。
DEFAULT:默认5V或3.3V为基准电压(以Arduino板的电压为准)。
INTERNAL:低电压模式,使用片内基准电压源(Arduino Mega无此选项)。
INTERNAL1V1:低电压模式,以1.1V为基准电压(此选项仅针对Arduino Mega)。
INTERNAL2V56:低电压模式,以2.56V为基准电压(此选项仅针对Arduino Mega)。
EXTERNAL:扩展模式,以AREF引脚(0~5V)的电压作为基准电压,其中AREF引脚的位置如图2.1所示。
图2.1 AREF引脚的位置
设置模拟输入引脚的基准电压为默认值的语句如下:
analogReference (DEFAULT);
注意
使用AREF引脚上的电压作为基准电压时,需接一个5kΩ的上拉电阻,以实现外部和内部基准电压之间的切换。但总阻值会发生变化,因为AREF引脚内部有一个32kΩ电阻,接上拉电阻后会产生分压作用,因此,最终AREF引脚上的电压为,Varef为AREF引脚的输入电压。
2.2.2 analogRead(pin)
analogRead函数的作用是从指定的模拟引脚读取值,读取周期为100μs,即最大读取速度可达每秒10000次。参数pin表示读取的模拟输入引脚号,返回值为int型(范围在0~1023)。
Arduino Uno主板有6个通道(Mega有16个)10位AD(模数)转换器,即精度为10位,返回值是0~1023。也就是说输入电压为5V的读取精度为5V/1024个单位,约等于每个单位0.049V(4.9mV)。输入范围和进度可通过analogReference()进行修改。
如输入电压为a,那么获取模拟输入引脚3的电压值的示例程序如下:
int potPin = 3; int value = 0; void setup() { Serial.begin(9600); } void loop() { value = analogRead(potPin) *a*1000/1023; // 输入电压是a Serial.println(value); // 输出电压值的单位为mV }
注意
对Arduino Uno而言,函数参数的pin范围是0~5,对应板上的模拟口A0~A5。其他型号的Arduino控制板以此类推。
2.2.3 analogWrite(pin,value)
analogWrite函数的作用是通过PWM的方式将模拟值输出到引脚,即调用analogWrite函数后,相应引脚将产生一个指定占空比的稳定方波(频率大约为490Hz),直到下一次调用该函数,可应用在LED亮度调节、电机速度控制等方面。
analogWrite函数是无返回值函数,有两个参数pin和value。参数pin表示将输出PWM的引脚,这里只能选择函数支持的引脚。对于大多数Arduino板(板载ATmega168或ATmega328),这个函数支持引脚3、5、6、9、10和11,对于ArduinoMega,它适用于2~13号引脚。参数value表示PWM输出的占空比,因为PWM输出位数为8,所以其范围在0(常闭)~255(常开)之间,对应占空比为0~100%。带PWM功能的引脚均标有波浪号“~”。
从引脚11输出PWM的示例程序如下:
int sensor=A0; int LED=11; int value; void setup() { Serial.begin(9600); } void loop() { value =analogRead(sensor); Serial.println(value,DEC); // 可以观察读取的模拟量 analogWrite(LED, value /4); // 读回的值范围是0~1023,结果除以4才能得到0~255的区间值 }
注意
引脚5和6的PWM输出将产生高于预期的占空比。这是因为millis()和delay()函数共享同一个内部定时器,使内部计时器在处理PWM输出时分心。这种情况一般出现在低占空比设置时,如0~10的情况下。还有些情况是占空比为0时,引脚5和6并没有关闭输出。
2.3 高级I/O
2.3.1 PulseIn(pin,state,timeout)
PulseIn函数用于读取指定引脚的脉冲持续的时间长度,该函数返回值类型为无符号长整型(unsigned long),单位为ms,如果超时没有读到的话,则返回0。
PulseIn函数包含3个参数pin、state、timeout。参数pin代表脉冲输入的引脚;参数state代表脉冲响应的状态,脉冲可以是HIGH或者LOW,如果是HIGH,则PulseIn函数将先等引脚变为高电平,然后开始计时,一直到变为低电平;参数timeout代表超时时间。
做一个按钮脉冲计时器,测一下按钮的时间,测测谁的反应快,看谁能按出最短的时间,其中按钮接引脚3。示例程序如下:
int button=3; int count; void setup() { pinMode(button,INPUT); } void loop() { count=pulseIn(button,HIGH); if(count!=0) { Serial.println(count,DEC); count=0; } }
2.3.2 shiftOut(dataPin,clockPin,bitOrder,val)
shiftOut函数的作用是将一个数据的一个字节一位一位地移出,它是一个无返回值函数。从最高有效位(最左边)或最低有效位(最右边)开始,依次向数据脚写入每一位,之后时钟脚被拉高或拉低,指示刚才的数据有效。
shiftOut函数包括4个参数dataPin、clockPin、bitOrder、val,其具体含义如下。
dataPin:输出每一位数据的引脚,引脚需配置成输出模式。
clockPin:时钟脚,当dataPin有数据时,此引脚电平会发生变化,引脚需配置成输出模式。
bitOrder:输出位的顺序,有最高位优先(MSBFIRST)和最低位优先(LSBFIRST)两种方式。
val:所要输出的数据值,该数据值将以byte形式输出。
从相应引脚输出500的示例程序如下,其中dataPin接引脚11,clockPin接引脚12,按最低位优先输出方式:
int dataPin = 11; int clockPin = 12; int data = 500; void setup() { pinMode(dataPin, OUTPUT); // 设置引脚为输出 pinMode(clockPin, OUTPUT); // 设置引脚为输出 } void loop() { shiftOut(dataPin,clockPin,LSBFIRST,data); // 移位输出低字节 shiftOut(dataPin,clockPin,LSBFIRST,data>>8); // 移位输出高字节 }
注意
shiftOut目前只能输出1个字节(8位),所以如果输出值大于255需要分两步。
2.4 时间函数
2.4.1 delay(ms)
delay函数是一个延时函数,它是一个无返回值函数,参数是延时的时长,单位是ms(毫秒)。
跑马灯的程序往往需用到delay函数,具体示例程序清单如下:
void setup() { pinMode(6,OUTPUT); // 定义为输出 pinMode(7,OUTPUT); pinMode(8,OUTPUT); pinMode(9,OUTPUT); } void loop() { int i; for(i=6;i<=9;i++) // 依次循环四盏灯 { digitalWrite(i,HIGH); // 点亮LED delay(1000); // 持续1s digitalWrite(i,LOW); // 熄灭LED delay(1000); // 持续1s } }
2.4.2 delayMicroseconds(μs)
delayMicroseconds函数也是延时函数,可以产生更短的延时,参数是延时的时长,单位是μs(微秒),其中1s=1000ms=1000000μs。
在delay(ms)的跑马灯程序中,延时程序delay(1000)(延时1秒)可以用delayMicroseconds(1000000)来代替。
2.4.3 millis()
millis函数用于获取单片机通电到现在运行的时间长度,单位是ms,该函数返回值类型为无符号长整型(unsigned long)。系统最长的记录时间为9小时22分,如果超出将从0开始。
millis是一个无参数函数,适合作为定时器使用,不影响单片机的其他工作,而使用delay函数期间无法做其他工作。
延时10秒后自动点亮接到引脚13的LED的示例程序清单如下:
int LED=13; unsigned long i,j; void setup() { pinMode(LED,OUTPUT); i=millis(); // 读入初始值 } void loop() { j=millis(); // 不断读入当前时间值 if((j-i)>10000) // 如果延时超过10s,点亮LED digitalWrite(LED,HIGH); else digitalWrite(LED,LOW); }
2.4.4 micros()
micros函数用于返回开机到现在运行的微秒值,该函数返回值类型为无符号长整型(unsigned long),70分钟将溢出。
显示当前的微秒值的示例程序清单如下:
unsigned long time; void setup() { Serial.begin(9600); } void loop() { Serial.print("Time: "); time = micros(); // 读取当前的微秒值 Serial.println(time); // 打印开机到目前运行的微秒值 delay(1000); // 延时1s }
2.5 中断函数
单片机的中断可概述为:由于某一随机事件的发生,单片机暂停原程序的运行,转去执行另一程序(随机事件),处理完毕后又自动返回原程序继续运行,其发生过程如图2.2所示,其中中断源、主程序、中断服务程序简述如下。
图2.2 中断发生的过程
中断源:引起中断的原因,或能发生中断申请的来源。
主程序:单片机现在运行的程序。
中断服务程序:处理中断事件的程序。
2.5.1 interrupts()和noInterrupts()
在Arduino中,interrupts函数与noInterrupts函数分别负责打开与关闭总中断,这两个函数均为无返回值函数,无参数。
2.5.2 attachInterrupt(interrput,function,mode)
attachInterrupt函数用于设置外部中断,有3个参数,分别表示中断源、中断处理函数和触发模式,它们的具体含义如下。
中断源:可选0或者1,对应2或者3号数字引脚。
中断处理函数:指定中断的处理函数,是一段子程序,当中断发生时执行该子程序部分,其中参数值为函数的指针。
触发模式:有四种类型——LOW(低电平触发)、CHANGE(变化时触发)、RISING(低电平变为高电平触发)、FALLING(高电平变为低电平触发)
数字引脚D2口接按钮开关,D4口接LED1(红色),D5口接LED2(绿色),LED3为板载LED灯,每秒闪烁一次。使用中断0来控制LED1,中断1来控制LED2。按下按钮,马上响应中断,由于中断响应速度快,LED3不受影响,继续闪烁。该示例的程序清单如下:
volatile int state1=LOW,state2=LOW; int LED1=4; int LED2=5; int LED3=13; // 使用板载的LED灯 void setup() { pinMode(LED1,OUTPUT); pinMode(LED2,OUTPUT); pinMode(LED3,OUTPUT); attachInterrupt(0,LED1_Change,LOW); // 低电平触发 attachInterrupt(1,LED2_Change,CHANGE); // 任意电平变化触发 } void loop() { digitalWrite(LED3,HIGH); delay(500); digitalWrite(LED3,LOW); delay(500); } void LED1_Change() { state1=!state1; digitalWrite(LED1,state1); delay(100); } void LED2_Change() { state2=!state2; digitalWrite(LED2,state2); delay(100); }
2.5.3 detachInterrupt(interrput)
detachInterrupt函数用于取消中断,参数interrupt表示所要取消的中断源。
2.6 串口通信函数
Arduino的串口通信是通过在头文件HardwareSerial.h中定义一个HardwareSerial类的对象serial,然后直接使用类的成员函数来实现的。
2.6.1 Serial.begin()
Serial.begin函数用于设置串口的波特率,波特率是指每秒传输的比特数,除以8可得到每秒传输的字节数。一般的波特率有9600、19200、57600、115200等。
2.6.2 Serial.available()
Serial.available函数用来判断串口是否收到数据,该函数返回值为int型,不带参数。
2.6.3 Serial.read()
Serial.read用于将串口数据读入,该函数返回值为int型,不带参数。
2.6.4 Serial.print()
Serial.print函数用于从串口输出数据,数据可以是变量,也可以是字符串。
2.6.5 Serial.printIn()
Serial.printIn函数的功能与Serial.print函数类似,都是从串口输出数据,只是Serial.printIn函数多了回车换行功能。
从串口输出“I have received!”字符的示例程序清单如下:
int x=0; void setup() { Serial.begin(9600); // 波特率9600 } void loop() { if(Serial.available()) { x=Serial.read(); Serial.print("I have received!"); Serial.printIn(x,DEC); // 输出并换行 } delay(200); }
2.7 数学库
2.7.1 min(x,y)
min函数的作用是返回x、y两者的最小值。
2.7.2 max(x,y)
max函数的作用是返回x、y两者的最大值。
2.7.3 abs(x)
abs函数的作用是返回x的绝对值,可以将负数转为正数。
2.7.4 三角函数
三角函数包括sin(rad)、cos(rad)、tan(rad),分别返回rad的正弦值、余弦值和正切值,返回值为double型。
2.7.5 random(small,big)
random函数用于生成一个随机数,其两个参数small和big决定了该随机数的范围,该函数的返回值为long型。
随机生成从0到100以内的整数的示例程序清单如下。
long x; void setup() { } void loop() { x=random(0,100); }