×

简单的Arduino光学斩波器

消耗积分:0 | 格式:zip | 大小:0.33 MB | 2023-02-09

分享资料个

描述

光学斩波器是一种周期性地中断外部光束的装置。虽然在科学实验室中使用了几种不同类型的光束斩波器,但这类似于光学快门,它会定期打开和关闭以允许光线按规定的时间间隔通过。这种类型的斩波器可以在许多类型的实验(例如光电化学)中实现一致且可重复的暗与光比较。有关使用光斩波器的一种实验的简要示例,请考虑对用 Pt 或 CoPi 装饰的氧化铁 (III) 进行的光电流密度测量,与黑暗中的电流密度相比,光照下的光电流明显增加。光学斩波器可以实现这种类型的比较。

Simple Arduino Optical Chopper 本质上是一个光学快门,它通过用伺服臂(10 度)阻挡光束并以设定的间隔打开光束(90 度)来操作。这是我以前通过使用一张纸或纸板并在密切监视秒表的同时手动阻挡光束来完成的任务(令人沮丧)。与手动操作相比,使用 arduino 和伺服电机可以进行更可重复的实验和更一致的斩波间隔。最重要的是,我不必一直站在实验旁边。

一般操作

按一次按钮开关启动斩波器程序。该程序包括两个阶段:安静时间和波束斩波。安静时间的量由变量 quietTime 指定,可用于允许在实验开始时延长“黑暗”时间(例如,允许电流密度达到光电化学实验中的基线)。在安静时间结束时,斩波阶段在总时间的剩余时间(由变量 totalTime 指定)开始,伺服电机以变量 chopInterval 指定的时间间隔在闭合角和打开角之间交替。

当到达总时间后,程序结束,伺服臂返回到闭合角度。按一次按钮开关可随时停止正在运行的程序,将伺服电机设置到指定的闭合角度。arduino 的板载 LED 13 用于监控程序当前是否正在运行。

该项目分为两部分,第一部分演示如何构建简单光学斩波器的基本版本,第二部分添加 LCD 等功能以查看指定的时间间隔和电位器以操纵这些间隔而不更改它们代码。您可以通读教程或在附件中找到这两部分的原理图和代码。

简单的光学斩波器

本节将介绍如何构建和编码光斩波器所需的最低限度。这个斩波器由三个主要部件组成:一个伺服器,它在物理上进行斩波;一个触觉按钮,用于启动和停止印章程序;和 arduino,它处理两者之间的逻辑。

 
pYYBAGPjQViAXjCXAA3shS4stbQ728.jpg
图 1:光斩波器的简单版本,由单个伺服器和按钮开关组成。
 

 

 
poYBAGPjQVqAPZ9NAAEzFQsnIqU662.jpg
图 2. 简单光斩波器的接线图。
 

首先,根据图 2 将伺服电机连接到面包板和 arduino。请注意,我的特定伺服与 Fritzing 接线图不匹配,因此请密切注意您的引脚分配。包括一个桥接伺服器 5V 和 GND 引脚的去耦电容器。当伺服电机开始运动时,它消耗的电流比它已经运动时要大,从而导致电路板上的电压下降。仔细检查电容器的阴极是否接地。

简单的光学斩波器代码

在设置功能之前,我们必须包含伺服库和变量以跟踪运行时间和斩波间隔。在这里,我将安静时间、斩波间隔和总时间分别设置为 2 分钟、4 秒和 4 分钟。本质上,我们希望程序在斩波器关闭的情况下运行 2 分钟,然后开始斩波,每 4 秒在打开和关闭之间切换一次,直到达到 4 分钟的总运行时间。这些是我的应用程序的最佳值,但显然可以为您的应用程序随意修改它们。

#include 
Servo myServo; 
unsigned long quietTime = 120000; 
unsigned long chopInterval = 4000; 
unsigned long totalTime = 240000; 

下一组变量将存储程序的不同状态(例如,斩波程序是否正在运行,是否按下开关,经过的时间等)

// variable used to indicate that the chop program is running 
// value of 1 or true means program is running 
int programRunning = 0;
// previous state of the start button 
int previousSwitchState = 0; 
// start button input pin 
const int startButtonPin = 8; 
// onboard LED used to indicate that chop program is running 
const int ledPin = 13; 
// time of previous interval 
unsigned long previousMillis = 0; 
// time that the start button was pressed 
unsigned long programStartTime = 0; 
// servo angles for both open and closed positions 
const int closedAngle = 10; 
const int openAngle = 90; 

光学斩波器将在两种不同的“状态”下运行,运行和不运行。触觉按钮开关将允许我们在两者之间切换,当前状态将存储在变量中:programRunning。

下一个变量不太直观。由于 arduino 的循环功能每秒运行数千次,我们不可能在一个程序循环中按下按钮,因此最好监视连接到按钮的引脚上信号从低电平到高电平的变化,因此我们将比较previousSwitchState 与代码后面的当前按钮状态。

该按钮将连接到引脚 8,我们将使用 LED 13 作为印章程序正在运行的板载视觉指示器。

因为斩波程序在很大程度上依赖于时间间隔,所以我们必须跟踪两者:按下开始按钮的时间以及自上次更改伺服位置以来已经过了多长时间。为此,我们定义了 previousMillis 和 programStartTime。

最后,我们定义打开和关闭对应的伺服角度(即光束畅通,或光束被阻挡)。90 度和 10 度最适合我使用的伺服。

现在让我们看看设置函数:

void setup() { 
  // pin 8, the start button is an input 
  pinMode(startButtonPin, INPUT); 
  // pin 13, the program running indicator LED is an output 
  pinMode(ledPin, OUTPUT); 
  // servo is attached to pin 9 
  myServo.attach(9); 
  // console for diagnostics 
  //Serial.begin(9600)
} 

这里没有什么太复杂的。开始按钮连接到引脚 8,我们使用引脚 13 作为印章程序正在运行的指示器(否则在安静的时候可能很难分辨,这些触觉按钮切换很容易不小心按两次),我们正在听写通过引脚 9 确定伺服的位置。最后,我在注释中包含了 Serial.begin() 函数,这对于故障排除很有用。

现在让我们处理代码的核心部分,即循环函数:

void loop() { 
  // gets the current runtime in milliseconds 
  unsigned long currentMillis = millis(); 
  // reads the start button switch state 
  int switchState = digitalRead(startButtonPin);
}

要通过循环开始每次运行,我们需要根据 arduino 的内部时钟以及开关是否被按下来跟踪当前时间。

接下来让我们编写开始按钮的行为代码,

// start/stop button
if (switchState != previousSwitchState) {
  if (switchState == HIGH) {
    programStartTime = currentMillis;
    programRunning = !programRunning;
  }
} 

由于我们通过循环在每次迭代中检查开关的状态,因此我们可以将其与上一次迭代期间的开关状态进行比较,寻找最初按下时从 LOW 到 HIGH 的变化。注意:我们将在循环结束时将 previousSwitchState 的值分配给 currentSwitchState,以便在下一次迭代中进行有意义的比较。

如果开关状态从 LOW 变为 HIGH,则将开始时间分配给当前时间并切换 programRunning 的值。

接下来,我们将解决 chop 程序运行时的行为(programRunning 为 true)。

// if the program running state is true as a result of pressing the start button 
if (programRunning) { 
  //turn the program running indicator LED on 
  digitalWrite(ledPin, HIGH); 
  // if quiet time has passed and total time has not been exceeded 
  if (currentMillis >= programStartTime + quietTime) { 
    // if the current time is still less than the total run time 
    if (currentMillis <= totalTime + programStartTime) { 
      // if the interval time has passed 
      if (currentMillis - previousMillis >= chopInterval) { 
        // stores the current time as previous time 
        // switches the state of the servo motor 
      }
    }
  }
}

一旦 chop 程序开始运行,我们的程序将做一些不同的事情:倒计时指定的安静时间,决定伺服器何时允许光束通过,确定程序何时结束。嵌套条件包含打开伺服必须满足的要求。首先,安静的时间一定已经结束;第二,程序一定不能超过规定的总运行时间;最后,定义的斩波间隔必须已经过去。

如果满足所有这些条件,我们检查舵机的位置,如果它处于关闭角度,我们通过将以下代码添加到上面最内层的条件来打开它:

// stores the current time as previous time
previousMillis = currentMillis;
if (myServo.read() == closedAngle) {
  myServo.write(openAngle);
} else {
  myServo.write(closedAngle);
}

我们还想将之前的 Millis 变量(我们用来确定是否超过斩波间隔的变量)分配给当前时间。

但是,如果斩波间隔还没有过去怎么办?或者如果安静时间还没有结束?我们希望光斩波器如何工作?

查看每个条件,如果安静时间尚未结束,舵机应保持其关闭角度。如果我们已经超过了程序的总运行时间(即程序刚刚结束),舵机应该处于关闭角度并且我们应该将 programRunning 变量的值更改为 false。最后,如果斩波程序没有运行,我们希望 LED 指示灯 (13) 熄灭并且伺服移动到关闭位置。我们可以通过填写以下 else 语句来完成此操作:

// if the program running state is true as a result of pressing the start button 
if (programRunning) { 
  //turn the program running indicator LED on 
  digitalWrite(ledPin, HIGH); 
  // if quiet time has passed and total time has not been exceeded 
  if (currentMillis >= programStartTime + quietTime) { 
    // if the current time is still less than the total run time 
    if (currentMillis <= totalTime + programStartTime) { 
      // if the interval time has passed 
      if (currentMillis - previousMillis >= chopInterval) { 
        // stores the current time as previous time 
        previousMillis = currentMillis; 
        if (myServo.read() == closedAngle) { 
          myServo.write(openAngle); 
        } else { 
          myServo.write(closedAngle); 
        } 
      } 
      // the servo returns to original position and the program is stopped 
      // when total run time is reached 
    } else { 
      myServo.write(closedAngle); 
      programRunning = !programRunning; 
    } 
  // servo remains in original position if still in quiet time   
  } else { 
    myServo.write(closedAngle); 
  } 
// servo remains in original position when chopper program is not running   
} else { 
  digitalWrite(ledPin, LOW); 
  myServo.write(closedAngle); 
}  

这是光学斩波代码的核心。我们需要包括最后一行,它与开始/停止按钮有关。我们需要包括,

previousSwitchState = switchState;

这允许我们通过存储它并在循环的下一次迭代中将其与 switchState 的值进行比较来监视按钮按下,如前所述。

就是这样!这就是基本光学斩波器所需的全部!继续阅读以添加 LCD 显示屏以及即时读取和修改斩波间隔、运行时间和安静时间的能力……

带 LCD 和电位器的光斩波器

基本的光学斩波器为您的光化学应用增加了一致性,但如果能够即时更改时间并读取斩波程序中剩余的时间量而不是进入代码会更方便。我们将使用 LCD 和一系列电位计来添加此功能。

首先,连接 LCD 屏幕并将其接线,如图 3 和 4 所示。如果您对为什么需要这么多电线感兴趣,请查看adafruit lcd 教程

 
poYBAGPjQa2AXeW0AA1y1RIi0A8886.jpg
图 3. 附有 LCD 屏幕的简单光学斩波器。
 

 

 
pYYBAGPjQbCAD6-7AAGQpzU6jJM388.jpg
图 4. LCD 接线图。
 

最终,我们将能够实时查看 chop 程序的剩余时间。但这还不是全部......通过添加一系列电位器,我们将能够调整安静时间的长度、程序运行时间和只需转动一个旋钮(或十字头螺丝刀,如果你有和我一样的锅)。因此,下一步是添加电位器并将它们连接起来,如下所示:

 
pYYBAGPjQbeAVQnQAAEgYgL51s0200.jpg
图 5. 电位器接线图。未显示:其他一切。
 

此时,您的斩波器应如下所示:

 
poYBAGPjQf6AK8hDABH83mVli30023.jpg
图 6. 带有 LCD 和电位器的完整 arduino 光斩波器。
 

现在一切都已连接好,让我们修改之前的代码以适应新功能。

带LCD和电位器的光斩波器代码

我们将首先从花盆开始,但最好继续将 arduino LCD 库添加到我们的 arduino 代码的顶部,

#include

此外,我们需要告诉 arduino 我们是如何连接到 LCD 的(即哪些引脚)。在对 LCD 库的调用下面添加这一行,

LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

好吧,让我们处理电位器。由于我们将根据电位器的可变电阻更改变量 quietTime、chopInterval 和 totalTime 的值,因此让我们删除之前给它们的值。此外,我还添加了另一个变量 programTime,它将存储 chop 程序在安静时间结束后运行的时间长度。用锅来控制这个时间比总时间更直观。您的代码现在应该如下所示:

// include the servo library and assign to myServo 
#include 
Servo myServo; 
#include 
LiquidCrystal lcd(2, 3, 4, 5, 6, 7); 
// quiet time, chop interval, and run time defined in milliseconds 
unsigned long quietTime; 
unsigned long chopInterval; 
unsigned long programTime; 
unsigned long totalTime; 

此外,我们需要将模拟输入引脚分配给一个变量,该变量指示连接的电位器正在修改的内容:

// pin assignments for the potentiometers 
int const chopIntervalPin(A0); 
int const programTimePin(A1); 
int const quietTimePin(A2); 

这就是变量。下一个代码修改发生在 chop 程序未运行时。当 chop 程序未运行时,我们希望 arduino 将来自电位器的模拟值转换为毫秒,以用于 quietTime、programTime 和 chopInterval。看看我们在 else 条件中添加了什么。

// if the program running state is true as a result of pressing the start button 
if (programRunning) { 
 // do all that stuff we described previously   
} else { 
  digitalWrite(ledPin, LOW); 
  myServo.write(closedAngle); 
  // the potentiometers that manipulate the time variables 
  // quiet time between 0 and 300 seconds 
  quietTime = analogRead(quietTimePin); 
  quietTime = map(quietTime, 0, 1023, 0, 10); 
  quietTime *= 30000; 
  // chop time between 0 and 300 seconds 
  programTime = analogRead(programTimePin); 
  programTime = map(programTime, 0, 1023, 1, 10); 
  programTime *= 30000; 
  // chop interval between 1 and 10 seconds 
  chopInterval = analogRead(chopIntervalPin); 
  chopInterval = map(chopInterval, 0, 1023, 1, 10); 
  chopInterval *= 1000; 
  totalTime = quietTime + programTime;  
}  

映射函数非常适合将模拟值从原始 0 到 1023 范围转换为 1 到 10“增量”。通过将这些值乘以毫秒值,我们定义了每个电位器的增量幅度(例如 quietTime 和 programTime 为 30s,chopInterval 为 1s)。我发现这种方法效果最好,因为直接映射到毫秒(例如 0 到 300000)会导致难以将电位计设置为精确时间,例如恰好 4 分钟或 2 秒。相反,这种方法为我们提供了很好且容易使用的时间增量。

如果我们不知道要将它们更改为什么,那么能够在实验之间更改时间值毫无意义。在本节中,我们将为 LCD 添加代码。

我们已经添加了 LCD 库并告诉 arduino 使用哪些引脚与 LCD 通信,但是我们需要添加,

lcd.begin(16, 2);

命令设置功能,它启动显示并告诉arduino显示有多大。

与 chop 程序本身类似,我们希望将 LCD 编程为在两种状态下运行:“开”和“关”。当斩波程序运行时,我们希望LCD显示还剩多少安静时间或斩波时间。为此,我添加了两个条件语句,一个将显示剩余安静时间,一个将显示剩余切割时间:

if (programRunning) {
  digitalWrite(ledPin, HIGH);
  if ((currentMillis - programStartTime) % 500 == 0) {
    // if quiet time has passed and total time has not been exceeded
    if (currentMillis >= programStartTime + quietTime) {
      lcd.clear();
      lcd.print("Quiet Time:"); 
      lcd.setCursor(12, 0); 
      lcd.print((quietTime - (currentMillis - programStartTime)) / 1000); 
    }
  }
}

最初,我发现 LCD 刷新方式很快使它难以阅读,因此我添加了,

if ((currentMillis - programStartTime) % 500 == 0)

有条件的,因此显示每秒仅更新两次。刷新后,LCD 清除其显示,打印“安静时间:”或“运行时间:”,移动光标并以秒为单位打印剩余时间(我认为四舍五入到最接近的整秒)。

当斩波程序停止时,我们希望 LCD 动态显示斩波间隔、安静时间和总运行时间的值以及“程序已停止”消息。

在我们计算出电位器的值后,我们可以添加以下代码:

if (currentMillis % 500 == 0) { 
  lcd.clear(); 
  lcd.print("Program Stopped"); 
  lcd.setCursor(0, 1); 
  lcd.print("Q:"); 
  lcd.print(quietTime / 1000); 
  lcd.print(" R:"); 
  lcd.print(programTime / 1000); 
  lcd.print(" I:"); 
  lcd.print(chopInterval / 1000); 
} 

与上面类似,这会清除显示并显示 quietTime、programTime 和 chopInterval 变量的值,因为它们已更改。

在这一点上,你应该有一个像这样工作的显示器,

 

就是这样!现在剩下的就是连接一块硬纸板或类似伺服电机的东西,将电机安装到环形支架上,开始您的实验,并且忘记再次手动切断光束!

我希望你喜欢这个教程!


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !