从零开始学Arduino编程

LaRiver创客开源2013/12/14

关于Arduino的历史、定位、功能等等的介绍,维基百科已有很详细的阐述,建议初学者都去看看,能对Arduino有更深入了解。本文基于Getting Started With Arduino(建议初学者购买),重点连载从零开始学习Arduino的基础编程知识和详细程序实现

关于Arduino的历史、定位、功能等等的介绍,维基百科已有很详细的阐述,建议初学者都去看看,能对Arduino有更深入了解。本文基于Getting Started With Arduino(建议初学者购买),重点连载从零开始学习Arduino的基础编程知识和详细程序实现,只针对刚拿到Arduino但基本无编程经验的同学,高手请绕行。

NO.1:点亮你的第一个LED灯

打开Arduino IDE,新建Sketch,参考以下代码,重点请看注释部分:

#define LED 13 //定义D13为LED正极口,将LED灯连接到板子上D12引脚
void setup() //此函数代码只会在程序刚开始时执行一次
{
  pinMode(LED, OUTPUT);  //定义LED也就是D13引脚模式为输出
}

void loop() //此程序在会Arduino通电后反复执行
{
  digitalWrite(LED, HIGH);  //输出13引脚信号为HIGH即高电平,开
  delay(1000); //持续1S
  digitalWrite(LED, LOW); //输出13引脚信号为LOW即低电平,关
  delay(1000); //持续1S
}

将此代码上传至Arduino并连接好LED灯,你就可以看到LED在闪烁。

NO.2 添加开关来控制LED(GND端串联10K欧姆电阻,+5V)

#define LED 13 //定义LED引脚为D13
#define BUTTON 7 //定义开关BUTTON引脚为D7

int val = 0; //变量val,并赋值0,用来存储按钮状态

void setup()
{
  pinMode(LED, OUTPUT); //LED引脚设置为输出模式
  pinMode(BUTTON, INPUT); //BUTTON引脚设置为输入模式
}

void loop()
{
  val = digitalRead(BUTTON); //读取BUTTON数值
  if (val == HIGH)//当val为HIGH的情况下
  {
    digitalWrite(LED,HIGH); //点亮LED
  }else{
    digitalWrite(LED, LOW); //关闭LED
  }
}

上传看看效果。

NO.3:打开按钮后LED常亮

#define LED 13 //定义LED引脚为D13
#define BUTTON 7 //定义开关BUTTON引脚为D7

int val = 0; //变量val,并赋值0,用来存储按钮状态
int state = 0; //变量state,定义0表示关闭,1表示打开

void setup()
{
  pinMode(LED, OUTPUT); //LED引脚设置为输出模式
  pinMode(BUTTON, INPUT); //BUTTON引脚设置为输入模式
}

void loop()
{
  val = digitalRead(BUTTON); //读取BUTTON数值
  if (val == HIGH) //当val为HIGH的情况下
  {
    state = 1 - state;
  }
  if(state == 1)
  {
    digitalWrite(LED, HIGH); //打开LED
  }else{
    digitalWrite(LED, LOW); //LED关闭状态
  }
}

上传看效果,你会发现会出现当你按开关LED灯关闭的问题,原因是程序中使用变量state储存0或者1来记忆灯的状态,当按下按钮时,state已被赋值为1,所以当Arduino再去读取开关状态时,当开关状态为HIGH,此时state被再次赋值为0,为关闭状态。

这其实是程序设计缺陷,如果来解决?继续往下:

#define LED 13 //定义LED引脚为D13
#define BUTTON 7 //定义开关BUTTON引脚为D7

int val = 0; //变量val,并赋值0,用来存储按钮状态
int old_val = 0; //存储变量val的上一个值
int state = 0; //变量state,定义0表示关闭,1表示打开

void setup()
{
  pinMode(LED, OUTPUT); //LED引脚设置为输出模式
  pinMode(BUTTON, INPUT); //BUTTON引脚设置为输入模式
}

void loop()
{
  val = digitalRead(BUTTON); //读取BUTTON数值
  if ((val == HIGH) &&(old_val == LOW)) //当val为HIGH并且old_val为LOW的情况下
  {
    state = 1 - state;
  }

  old_val = val; //val此时已经是旧的了,让数值赋给old_var

  if(state == 1)
  {
  digitalWrite(LED, HIGH); //打开LED
  }else{
  digitalWrite(LED, LOW); //LED关闭状态
  }
}

写到此时,我已完全被LED灯闪瞎了眼。程序依然没有正常工作!现在的问题是由于按钮的机械构造造成的,按钮由两片金属的接触实现电路的导通,在现实中,金属之间的接触并不是完全密合,尤其是金属尚未到底的瞬间和按钮弹回的时候,会产生杂波。

当杂波出现的时候,Arduino会接受到一连串开关交错的信号。要克服这个杂波,有几种不通方式,对上边的简单程序来讲,只需要在程序中加入10~50毫秒的延迟指令就可以改善。参考以下代码:

#define LED 13 //定义LED引脚为D13
#define BUTTON 7 //定义开关BUTTON引脚为D7

int val = 0; //变量val,并赋值0,用来存储按钮状态
int old_val = 0; //存储变量val的上一个值
int state = 0; //变量state,定义0表示关闭,1表示打开

void setup()
{
  pinMode(LED, OUTPUT); //LED引脚设置为输出模式
  pinMode(BUTTON, INPUT); //BUTTON引脚设置为输入模式
}

void loop()
{
  val = digitalRead(BUTTON); //读取BUTTON数值
  if ((val == HIGH) &&(old_val == LOW)) //当val为HIGH并且old_val为LOW的情况下
  {
    state = 1 - state;
    delay(50);
  }

  old_val = val; //val此时已经是旧的了,让数值赋给old_var

  if(state == 1)
  {
    digitalWrite(LED, HIGH); //打开LED
  }else{
    digitalWrite(LED, LOW); //LED关闭状态
  }
}

NO.4:使用PWM的方式来控制LED灯亮度

PWM被成为脉冲宽度调制,如果我们改变延迟函数的数值,直到你看不出LED灯亮和灭的时间间隔为止,你会发现LED灯亮度只有原来的50%,如果继续改变时间间隔,你会发现灯的亮度只有原来的25%。这就是PWM。

Arduino电路板上使用的处理器可以在处理其他事情的同时点亮3各LED使其闪烁,这部分PWM输出接口为D9、D10、D11,这3个输出接口可以由analogWrite()函数控制。analogwrite()指令中PWM参数的可设范围为0~255,255代表最亮,0代表最暗,如果你使用analogwrite(9, 128)则代表D9引脚接的LED灯亮度为50%。

逐渐改变亮度的LED灯(使用LED等串联一个470~1K欧姆的电阻,并接到D9引脚,负极接GND):

#define LED 9 //定义LED为D9引脚

int i = 0; //定义变量i,用来从上到下计数

void setup()
{
  pinMode(LED,OUTPUT); //LED为输出
}

void loop()
{
  for(i = 0; i < 255 ; i++){ //循环255次,从小到大
    analogWrite(LED, i); //PWM参数,亮度从小到大
    delay(10); //延迟10毫秒,因为anaWrite()参数执行瞬间完成
  }
  for(i = 255; i > 0; i--){ //循环255次,从大到小
    analogWrite(LED, i); //PWM参数,两大从大到小
    delay(10); //延迟10毫秒
  }
}

上传看看,是不是很神奇?

NO.5:按下松下按钮开关LED,长按开关调节LED灯亮度(NO.2+NO.4两个小电路积木结合)

#define LED 9    // 定义LED为D9引脚
#define BUTTON 7 //定义按钮为D7引脚

int val = 0;     //变量val存储按钮数值状态
int old_val = 0; //存储val的上一个数值
int state = 0;   //0为关闭LED,1为打开LED
int brightness = 128;        //存储亮度值
unsigned long startTime = 0; //存储按下按钮的开始时间

void setup() {
  pinMode(LED, OUTPUT);   //LED引脚为输出
  pinMode(BUTTON, INPUT); //BUTTON引脚为输入
}

void loop() {
  val = digitalRead(BUTTON); //读取按钮数值并存储到val变量
  if ((val == HIGH) && (old_val == LOW)) {
    state = 1 - state;
    startTime = millis(); // millis()是Arduino的时钟,存储最后一次按下按钮的时间,单位毫秒
    delay(10);
  }
  if ((val == HIGH) && (old_val == HIGH)) {
    if (state == 1 && (millis() - startTime) > 500) { //如果按钮被按了超过500毫秒
      brightness++; // 亮度值递增
      delay(10);
    if (brightness > 255) { //如果亮度值超出255
      brightness = 0; //重置亮度为0
      }
    }
  }

old_val = val; //执行了一次之后,val已变成旧值,存储到old_val

if (state == 1) {
  analogWrite(LED, brightness); //以当前亮度值打开LED
  }else{
  analogWrite(LED, 0); //关闭LED
  }
}

是不是很酷?当然,你可以尝试把开关换成其他敏感元件,如光敏电阻、热敏电阻等其他传感器,来制作更有趣的场景。

以上我们进行的都是基于数字I/O引脚的试验,接下来开始尝试模拟I/O引脚(模拟引脚默认为输入状态)。模拟输入引脚analog in不仅可以读出是否有电压输入(数字引脚只能读出有或无),还能读出电压值的大小。我们使用analogRead()函数来读取输入电压值,该参数的返回值范围为0~1023,分别对应0~5V间的电压。如,在模拟口输入2.5V电压,analogRead()返回的值为512。以下开始模拟口的尝试:

NO.6:根据模拟输入值大小来设定LED闪烁频率

硬件环境需要先使用光敏电阻串联10K欧姆电阻,串联电阻一端接GND,一端接5V引脚,同时,在10K欧姆电阻的并联方向引出A0引脚接口。LED灯正极接D13,负极串联1K欧姆电阻接GND。

#define LED 13 //定义D13引脚为LED

int val = 0; //变量val,存储模拟口读取的数值

void setup(){
  pinMode(LED,OUTPUT); //LED为输出
}

void loop(){
  val = analogRead(0); //读取A0并赋值给val
  digitalWrite(LED,HIGH); //点亮LED
  delay(val); //延迟时间为val的数值
  digitalWrite(LED,LOW); //关闭LED
  delay(val); //延迟时间为val的数值
}

一闪一闪亮晶晶!

NO.7:根据模拟输入值来调整LED灯亮度

#define LED 9 //定义D9引脚为LED,D9为PWM引脚

int val = 0; //变量val,存储模拟口读取的数值

void setup(){
  pinMode(LED,OUTPUT); //LED为输出
}

void loop(){
  val = analogRead(0); //读取A0并赋值给val
  analogWrite(LED, val/4); //PWM输出最大值为255
  delay(10); //延迟10毫秒
}

有了上边的编程基础,你可以变换出任意的积木式组合、扩展性场景。

接下来看下Arduino的串口通信,通过串口通信,我们可以让Arduino和计算机或其他控制机之间互相通信,让Arduino把数据传回计算机,并能接收来自计算机的指令。要实现这一功能,可以在Arduino程序中通过Serail()函数,此函数包含了串口通信所需的所有程序代码。

NO.8:从计算机串口读取Arduino模拟传感器输入数据

硬件环境依照NO.7的光敏电阻电路,程序如下:

#define SERSOR 0 //定义传感器输入口为A0

int val = 0; //变量val来存储读取的数值

void setup(){
  Serial.begin(9600); //打开串口并设置通信波特率为9600
}

void loop(){
  val = analogRead(0); //从A0读取传感器反馈值并赋值给val
  Serial.println(val); //串口监视窗口显示传感器的反馈值
  delay(100); //每100毫秒发送一次数据
}

上传至Arduino,在Arduino工具菜单的串口监视窗口打开看看,你所看到的值可以通过0~5V换算成电压,根据各传感器的Datasheet,可以得出你想要的现实环境中的某项数值。