1、UNO R3的端口输出实验
1.1 实验介绍
对于单片机中最为经典的控制电路就是流水灯实验,流水灯也称作跑马灯,利用单片机的IO口输出高电平(HIGH)与低电平(LOW)进而控制外部电路中多个LED灯以不同时间频率的亮灭。在实验中使用UNO开发板控制外围电路中的6个LED灯,使其以1s的时间间隔依次亮起,其电路图如下所示:
在这里插入图片描述

1.2 核心代码

int Start= 2;
int Num = 6;
int Index = 0;
void setup()
{
	for (int i = Start; i < Start + Num; i ++) 
	{
		pinMode(i, OUTPUT);
	}
}
void loop()
{
	for (int i = Start; i < Start + Num; i ++) 
	{
	 	digitalWrite(i, LOW);
	}
	digitalWrite(Start + Index, HIGH);
	Index = (Index + 1) % NUM;
	delay(1000);
}

2、UNO R3的端口输入实验
2.1 实验介绍
对于Arduino UNO开发板的数字IO口不仅有输出高低电平的功能(如前一小节),同时其还可以读取外部电路的高电平(5V)以及低电平(0V)。需要注意的是UNO的读取范围需要满足其工作电压以及电流,因此很多不匹配的输入需要通过一定的电路将其转换成UNO可以识别的范围。其中最简单的实验就是对按键开关连接或断开状态的判断,当按压开关按下时,单片机读取的输入信号应为高电平;当开关断开是,输入信号为低电平。本实验使用按键控制板载LED灯的亮灭,其电路如下所示:
在这里插入图片描述

2.2 核心代码

int Led= 13;
int Switch = 7;
int Value = 0;
void setup(){
	pinMode(Led, OUTPUT);
	pinMode(Switch, INPUT);
}

void loop() {
	Value = digitalRead(Switch);
	if (HIGH == Value) 
	{
		digitalWrite(Led, LOW);
	} 
	else 
	{
		digitalWrite(Led, HIGH);
	}
}

3、步进电机实验
3.1 实验介绍
步进电机是一种将电脉冲信号转换成相应角位移或线位移的电动机。每当输入一个脉冲信号是,转子就转动一个角度或前进一步,其输出的角位移或线位移与输入的脉冲数成正比,转速与脉冲频率成正比。因此,步进电动机又被称为脉冲电动机。脉冲电机相对于其它电机最大的区别就是,其接收数字控制信号,因此使用单片机对其进行控制是非常方便的。本次实验使用的是五线四相型步进电机,其实物如下图中所示:
在这里插入图片描述

实验中还需要应用到的一个模块便是ULN2003步进电机驱动模块,它是一个大功率驱动芯片常用来驱动小型的五线四相电机,板载的LED灯可以指示步进电机的工作状态。IN1~IN4口连接于单片机的IO口,用以输出数字信号,通过模块将其转换为脉冲信号进而控制电机工作,其实物如下图中所示:

在这里插入图片描述

3.2 核心代码

int motor1=8;
int motor2=9;
int motor3=10;
int motor4=11;
void stepMotor(int thisStep);
void ctlstepper(int whatSpeed,int steps);
void setup() {
  // put your setup code here, to run once:
  pinMode(motor1,OUTPUT);
  pinMode(motor2,OUTPUT);
  pinMode(motor3,OUTPUT);
  pinMode(motor4,OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  ctlstepper(800,20);
}
void ctlstepper(int whatSpeed,int steps)
{
  int FX;//记录方向
  int stepNumber=0;//记录运行到8拍的第几步
  int stepsLeft=abs(steps);//记录运行步数,取绝对值
  unsigned long stepDelay=60L*1000L/64/whatSpeed; //依据速度计算时间
  long lastStepTime=0; //保存两步之间的时间
  if(steps>0)
  {
    FX=1;
  }
  if(steps<0)
  {
    FX=0;
  }
  while(stepsLeft>0)
  {
    if(millis()-lastStepTime>=stepDelay)
    {
      lastStepTime=millis();
      if(FX==1)
      {
        stepNumber++;
        if(stepNumber==64)
        {
          stepNumber=0;
        }
      }
      else
      {
        if(stepNumber==0)
        {
          stepNumber=64;
        }
        stepNumber--;
      }
      stepsLeft--;
      stepMotor(stepNumber%8);
    }
  }
}
void stepMotor(int thisStep)
{
  switch(thisStep)
  {
    case 0: //1000
      digitalWrite(motor1,HIGH);
      digitalWrite(motor2,LOW);
      digitalWrite(motor3,LOW);
      digitalWrite(motor4,LOW);
      break;
    case 1: //1100
      digitalWrite(motor1,HIGH);
      digitalWrite(motor2,HIGH);
      digitalWrite(motor3,LOW);
      digitalWrite(motor4,LOW);
      break;
    case 2: //0100
      digitalWrite(motor1,LOW);
      digitalWrite(motor2,HIGH);
      digitalWrite(motor3,LOW);
      digitalWrite(motor4,LOW);
      break;
    case 3: //0110
      digitalWrite(motor1,LOW);
      digitalWrite(motor2,HIGH);
      digitalWrite(motor3,HIGH);
      digitalWrite(motor4,LOW);
      break;
    case 4: //0010
      digitalWrite(motor1,LOW);
      digitalWrite(motor2,LOW);
      digitalWrite(motor3,HIGH);
      digitalWrite(motor4,LOW);
      break;
    case 5: //0011
      digitalWrite(motor1,LOW);
      digitalWrite(motor2,LOW);
      digitalWrite(motor3,HIGH);
      digitalWrite(motor4,HIGH);
      break;
    case 6: //0001
      digitalWrite(motor1,LOW);
      digitalWrite(motor2,LOW);
      digitalWrite(motor3,LOW);
      digitalWrite(motor4,HIGH);
      break;
    case 7: //1001
      digitalWrite(motor1,HIGH);
      digitalWrite(motor2,LOW);
      digitalWrite(motor3,LOW);
      digitalWrite(motor4,HIGH);
      break;
  }
}

4、超声波测距实验
4.1 实验介绍
超声波是振动频率高于20kHz的机械波,它具备频率高、波长短、绕射现象小、方向性好以及能够成为射线从而定向船舶的特点。超声波可以被使用到各种不同的场景中,超声波HC-SR04距离传感器模块就是其中的一种应用。HC-SR04模块性能稳定、测度距离精确,且使用简单。通过单片机的一个控制口向模块的发送口发送一个超过10us时间的高电平,就可以在模块接收口处对高电平输出的时间进行采集,最后通过一定的数学转换公式即可计算出所测量的距离值。
在这里插入图片描述

4.2 核心代码

int trigPin=3;
float cm;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(echoPin,INPUT);
  pinMode(trigPin,OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(trigPin,LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin,HIGH);
  delayMicroseconds(10);//延时微秒
  digitalWrite(trigPin,LOW);
  cm=pulseIn(echoPin,HIGH)/58.0;//读取引脚上的高电平脉冲,最大脉冲时间间隔为60秒,并且把结果赋值给变量
  cm=(int (cm*100.0))/100.0;
  Serial.println(cm);  
  delay(100);
}

5、LCD液晶显示实验
5.1 实验介绍
对于LCD1602液晶屏模块的详细介绍可以参见往期的文章,硬件电路的连接情况如下图中所示。在这个实验当中使用UNO开发板控制液晶屏上显示我们想要的字符,在Arduino中调用库函数<LiquidCrystal.h>便可以简单的实现这个功能。其对应的引脚接线如表中所示:

在这里插入图片描述

5.2 核心代码

#include <LiquidCrystal.h>
const int Rs = 3, En = 5, D4 = 10 D5 = 11, D6 = 12, D7 = 13;
LiquidCrystal lcd(Rs, EN, D4, D5, D6, D7);
void setup()
{
	lcd.begin(16, 2);
	lcd.print(“Hellow Arduino UNO!”);
}
void loop()
{
	lcd.setCursor(0, 1);
	lcd.print( mills()/1000 );
}

6、传感器实验

6.1 温度采集实验

LM35是一种已经得到了广泛使用温度传感器,其用法简单,仅需要单片机的一个模拟口便可读取传感器采集到的模拟值,然后通过一定的数学公式将其转换为实际的温度。其硬件电路连接图与核心代码如下所示:

在这里插入图片描述主要代码:

void loop() 
{
  // put your main code here, to run repeatedly:
  Serial.println(analogRead(LM35)*4.9/10);
  delay(1000);
}

6.2 RFID射频识别实验

本实验中使用RFID读卡器对电子标签进行识别,需要调用到 MFRC522库,这个库可以完成对RFID读卡器的一系列操作,其核心代码如下所示:

#include <MFRC522.h>
#include <SPI.h>
#define SS_PIN 10
#define RST_PIN 9
MFRC522 mfrc522(SS_PIN,RST_PIN);
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  SPI.begin();
  mfrc522.PCD_Init();
  Serial.println("Scan PICC to see UID and tye...");
}
void loop() {
  // put your main code here, to run repeatedly:
  if(!mfrc522.PICC_IsNewCardPresent())//如果不是新卡就返回
  {
    return;
  }
  if(!mfrc522.PICC_ReadCardSerial())//选择卡片失败
  {
    return;
  }
  mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
}

6.3 红外实验

本实验中采用的是如下图中的红外遥控器,以及红外接收传感器。在发送端使用遥控器按下按键,通过红外接收传感器可以在UNO开发板所在的接收端受到相应的按键信息。
在这里插入图片描述

#include <IRremote.h>
#include <IRremoteInt.h>
//res.value为接收的值,recv.resume()接收下一个值
int recvPin=2;
IRrecv recv(recvPin);
decode_results res;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  recv.enableIRIn();
}

void loop() {
  // put your main code here, to run repeatedly:
  if(recv.decode(&res))//判断是否收到数据
  {
    Serial.println(res);
  }
}

6.4 感光灯实验

本实验采用一个LED灯以及一个光敏电阻来完成感光灯的实验,当光敏电阻感受到不同光强是其阻值会发生变化,通过UNO采集这个变化值,最后呈现在LED的亮度变化上。

在这里插入图片描述

void loop()
{
	val=analogRead(potpin);
	Serial.println(val);
	analogWrite(ledpin, val);
	delay(10);
}

7、Arduino与Processing交互实验
7.1 实验介绍
Processing是一门开源编程语言和与之配套的的集成开发环境(IDE)的名称,其在电子艺术和视觉设计社区被用来教授编程基础,并运用与大量的新媒体和互动艺术作品中。在本次实验中,实现了Arduino通过串口控制Processing的相关操作。实现在Arduino UNO端利用A0模拟口读取声音传感器采集到的数据,然后通过串口发送到Processing客户端,最后将这些数据进行可视化的展示。

7.2 核心代码
Arduino端:

int sensorPin=A0;
int sensorValue=0;
void setup() {
  // put your setup code here, to run once:
  pinMode(sensorPin,INPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  sensorValue=analogRead(sensorPin);
  Serial.println(sensorValue,DEC);
}

Processing端:

import processing.serial.*;
Serial myPort; 
public static final char HEADER='h'; //从串口读取信息的第一个字符校验符
public static final short LF=10; //每次从串口读取信息的长度
float vol; //声音变量
float offset=100; //正弦曲线0点向y轴移动的值
float scaleVal=35;//曲线振幅
float angleInc=PI/60;//角速度
float angle=0;
float dx=0; //x轴偏移量
float under;//振幅的最小值
float high=200;//音量的最大值
String [] com;
void setup()
{
  //println(Serial.list());//获取当前可用串口列表
  //com=Serial.list();
  myPort=new Serial(this,"COM10",9600);//初始化myPort,读取第一个可用串口,比特率为9600
  myPort.bufferUntil('\n');   //从串行口读数据时遇到换行符则结束读取
  size(600,200);
  noStroke();
  frameRate(60);  //每秒刷新60次
}
void draw()
{  
  scaleVal=map(vol,under,high,0,100);
  background(204);
  fill(255,0,0);
  rect(20,20,20,20);//绘制一个矩形,鼠标单击时获取当前背景噪声,并以当前噪音为基准线
  fill(0);
  for(int posx=0;posx<=width;posx++)
  {
    float posy=offset+(sin(angle-dx)*scaleVal);
    rect(posx,posy,1,1);
    angle+=angleInc;
  }
  dx+=0.05;
  angle=0;
}
void serialEvent(Serial myPort)
{
  String message=myPort.readStringUntil(LF);
  if(message!=null){
    String []data=message.split(",");//将字符串信息用逗号分隔
    if(data[0].charAt(0)==HEADER){
      if(data.length>1)
      {
        vol=Integer.parseInt(data[1]);
        if(high<vol)
          vol=high;
        if(under>vol)
          vol=under;
      }
    }
  }
}
void mouseClicked()
{
  if(mouseX<40&&mouseX>20)
  {
    if(mouseY<40&&mouseY>20)
    {
      under=vol;
    }
  }
}

8、多线程库Scoop的使用
8.1 实验介绍
多线性,是指从软件或者硬件上实现多个线程并发执行的技术,也就是说单片机可以在同一时间执行多个线程,进而提升整体的处理性能。通过之前的实验掌握的主要是单片机的顺序编程,也就是说单片机是按照编写的代码一步一步执行,在这里就有一个先后的次序问题。但在有些情况我们需要在一段时间内执行多个功能,这个时候就需要用到了多线程库,因此在Arduino UNO开发板上实现多线程编程本质上是从软件而不是硬件上实现的。在使用这个库是,必须要在setup() 函数中调用mySccop.start()命令,同时还需要在loop()函数中调用yield()命令,关于对多线程创建人物的模板如代码中所示。

8.2 核心代码

#include <SCoop.h>

#define led1 LED_BUILTIN

defineTaskLoop(myTask1)    //第一个线程函数(不需要初始化函数,将loop函数写在{}里)
{ 
	Serial.println("hello from task1");
	sleep(1000); 
}

defineTask(myTask2)   //第二个线程函数
void myTask2::setup() 
{ 
	trace("task2 setup"); 
	pinMode(led1, OUTPUT);
}

void myTask2::loop()  
{ 
	Serial.println("led1 HIGH"); 
	digitalWrite(led1, HIGH); 
	sleepSync(500);
	Serial.println("led1 LOW");  
	digitalWrite(led1, LOW);  
	sleepSync(500); 
}

defineTimerRun(Timer1,100) //快速定义一个时钟,周期为100ms
{
	if (Serial.available()) 
	{ 
	     char c = Serial.read(); 
		 Serial.print(c);Serial.println(" key pressed"); 
		 if (c=='a') myTask1.pause();//任务一暂停
		 if (c=='b') myTask1.resume();//任务一重启
	}
}

void setup() 
{ 
	Serial.begin(57600); 
	while (!Serial); 
	mySCoop.start(); 
}
void loop()  
{ 
	Serial.println("do whatever you want here also"); 
	mySCoop.sleep(500); 
}