第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);
}