电源设计技术信息网站

订阅电子杂志   English   繁體中文   日本語   한국어

下载中心

创造发明Arduino入门指南

适用于罗姆传感器评估套件的轻量级 Arduino库

这篇文章来源于DevicePlus.com英语网站的翻译稿。

 

arduino library

@CoreStaff

罗姆传感器评估套件是一种兼容 Arduino 的扩展板 (Shield),配有以下8钟传感器:加速度计、压力传感器、地磁传感器、照度/接近传感器、颜色传感器、霍尔传感器、温度传感器和紫外线传感器。该传感器扩展板的设置指南请参阅 Arduino传感器 – ROHM传感器评估套件概览。单个传感器的 Arduino 库以及详细文档可以从官网上下载:http://www.rohm.com/web/global/sensor-shield-support

对于要用的每个传感器,官网所列的库都要求您在 Arduino 程序中包含另外一个库。如果您计划只使用一个或两个传感器,那么这样做行得通,但是如果您决定让单个 Arduino UNO 开发板同时处理多个传感器(比如6个传感器)呢?!

硬件

 

软件

  • Arduino IDE
  • GitHub – 罗姆MultiSensor库

上述链接是一个专门用于罗姆 MultiSensor 的GitHub程序库——该单个 Arduino 库能够让您控制罗姆传感器评估套件中的所有传感器,不需要单独包含每个传感器库,因此可以节省时间。此外,这个库允许您根据当前要求设置传感器——这是原罗姆库目前所缺乏的特性。该库的另一个优点就是符合 Arduino 1.5 IDE 规范。它还包括自定义语法高亮显示功能、详细的自述 (readme) 文件和大量实例!

与罗姆提供的库比较

我们假设您想用罗姆提供的库连接所有传感器。这不仅会导致 Arduino 程序相当长,而且还会造成后台运行许多不必要的运算。比如,每个库都有自己的I2C总线控制方法,但它们都相同。所有这些都会增加 Arduino 程序的整体大小和内存使用量。我们能不能只用一个I2C方法呢?下图显示了使用所需6个罗姆库时的程序编译器输出:

arduino library

图 1. 使用罗姆提供的库处理所有传感器时的编译器输出

 

9886 字节似乎不是很大,但是记住,该程序除了从传感器中提取数据并在串口上显示之外,没有做任何其他事情。这个数字告诉我们,尽管任务非常简单,但是却占用了三分之一的存储空间。这在添加更多功能时会造成空间问题。

现在,让我们比较一下使用罗姆 MultiSensor 库的程序的编译器输出,其功能完全一样:

 

arduino library

图 2. 使用罗姆  MultiSensor 库处理所有传感器时的编译器输出。
请注意闪存存储空间和内存需求都减少了。

您可以看到,我们节省了超过 2000 字节的闪存存储空间以及100字节的内存需求 !

同时使用多个传感器的5个步骤

让我们看一下上文提到的例子——在单个Arduino开发板上运行6个罗姆传感器。所用传感器包括:加速度计(kx022 – 1020)、压力传感器(BM1383GLV)、照度/接近传感器(RPR-0521RS)、颜色传感器(BH1745NUC)、温度传感器(BD1020HFV) 和紫外线传感器 (ML8511A)。

arduino library

图 3.同时使用6个传感器

使用这些传感器的理由非常简单:它们的工作电压都是3V。由于我们只能在扩展板上为所有传感器设置一种电压电平,因此我们必须使用大多数传感器支持的电压。使用罗姆MultiSensor库时,代码如下所示:

// definition
 
#define INCLUDE_KX022_1020
 
#define INCLUDE_BM1383GLV
 
#define INCLUDE_RPR_0521RS
 
#define INCLUDE_BH1745NUC
 
#define INCLUDE_BD1020HFV
 
#define INCLUDE_ML8511A
 
 
 
// inclusion
 
#include <RohmMultiSensor.h>
 
 
 
// instantiation
 
KX022_1020 acc;
 
BM1383GLV bar;
 
RPR_0521RS als;
 
BH1745NUC rgbc;
 
BD1020HFV temp;
 
ML8511A uv;
 
 
 
void setup() {
 
 Serial.begin(9600);
 
 Wire.begin();
 
 
 
 // initialization
 
 acc.init();
 
 bar.init();
 
 als.init();
 
 rgbc.init();
 
 temp.init(ANALOG_1);
 
 uv.init(ANALOG_2);
 
 
 
 Serial.println("X[g]\tY[g]\tZ[g]\tp[hPa]\tPS[cnt]\tALS[lx]\tR[-]\tG[-]\tB[-]\tC[-]\tt[dg C]\tUV[mW/cm^2]");
 
 
 
void loop() {
 
 //measurement
 
 float* accelValue = acc.measure();
 
 float pressValue = bar.measure();
 
 float psValue = als.measure(PS);
 
 float alsValue = als.measure(ALS);
 
 unsigned int* rgbcValue = rgbc.measure();
 
 float tempValue = temp.measure();
 
 float uvValue = uv.measure();
 
 
 
 Serial.print(accelValue[0]);  Serial.print('\t');
 
 Serial.print(accelValue[1]);  Serial.print('\t');
 
 Serial.print(accelValue[2]);  Serial.print('\t');
 
 Serial.print(pressValue);     Serial.print('\t');
 
 Serial.print(psValue);        Serial.print('\t');
 
 Serial.print(alsValue);       Serial.print('\t');
 
 Serial.print(rgbcValue[0]);   Serial.print('\t');
 
 Serial.print(rgbcValue[1]);   Serial.print('\t');
 
 Serial.print(rgbcValue[2]);   Serial.print('\t');
 
 Serial.print(rgbcValue[3]);   Serial.print('\t');
 
 Serial.print(tempValue);      Serial.print('\t');
 
 Serial.println(uvValue);
 
 
 
 delete[] accelValue;
 
 delete[] rgbcValue;
 
 
 
 delay(100);
 
}

Arduino IDE串行绘图仪(Serial Plotter)的输出如下图所示。当然,这种输出几乎无法用于任何实际用途。然而,它表明所有传感器都在工作并测量数据。

arduino library

图 4. 所有6个传感器的串行绘图仪输出

使用这个库时,您必须按照以下五个步骤进行配置:定义、包含、实例化、初始化和测量。这听起来比较复杂,所以我们接下来详细解释每一步。

1.定义

第一步就#define(定义)所有要使用的传感器。下面的步骤解释了为什么必须首先执行这些定义。

为了缩短代码,您可以使用一个预编程的简化定义。#define INCLUDE_ALL_1V8_SENSORS#define INCLUDE_ALL_3V0_SENSORS和#define INCLUDE_ALL_5V0_SENSORS。比如,#define INCLUDE_ALL_1V8_SENSORS将会定义所有推荐供电电压为1.8V的传感器。而电压为3V和5V的定义语句分别为:#define INCLUDE_ALL_3V0_SENSORS#define INCLUDE_ALL_5V0_SENSORS

2. 包含

这实际上就是您#include(包含)库头文件的地方。在此步骤之前必须完成所有传感器#define语句的原因很简单:头文件(罗姆MultiSensor.h)中包含了其他的传感器特定文件,而这必须以定义适当传感器为基础。这使得库可以根据用户需求动态改变其大小,从而节省Arduino闪存存储空间和内存需求。

3. 实例化

因为每个传感器都有自己的类,因此访问其方法之前,您必须创建该类的一个实例。该过程被称为实例化,您所要做的就是输入传感器类名,然后紧跟实例名。比如:

BM1383GLV bar;

该语句会创建一个BM1383GLV类实例,而且您可以通过名称bar访问该实例。

4. 初始化

每个传感器在使用前都必须初始化。您所要做的就是为每个传感器调用.init()方法。此方法在成功完成后返回0,如有任何问题,则返回1。您可以用以下代码检查所有传感器是否都已经成功初始化:

void setup() {
 
if(bar.init() == 1) {
 
 // there was an error, you have to fix it!
 
}
 
// everything went fine, you can start measuring!

 

.init()方法还有一个额外功能:更改传感器设置。您可以通过传递各种参数来改变传感器的行为。

比如我们想要改变压力传感器的工作模式。在默认模式下,传感器会测量200毫秒,然后返回平均值。如果我们想让传感器运行得更快(可能会变得不那么准确),我们可以使用以下参数调用.init()方法:

bar.init(BM1383GLV_CONTINUOUS_100_MS);

现在,测量只会在100 ms的工作模式下进行。每次重新启动传感器之后,所有设置都会被重置为默认值。这通常只与整个Arduino重置(即按重置按钮)相关,所以这并不是一个问题。

 

请参阅GitHub库参考 文件,以获得每个传感器当前已经实现的完整设置列表。

注:这些设置都是可选设置;通常,默认设置就已足够(即直接调用.init(),无需任何参数)。

5. 测量

此时,一切准备就绪,可以开始测量数据了。您可以通过调用所用传感器的.measure()方法执行此操作。该方法无需参数,其返回数据类型取决于传感器种类。有些传感器会返回单个数值——一个浮点数或者一个整数。

float pressValue = bar.measure();

上述语句示例会通过BM1383GLV传感器测量压力,并返回单位为hPa的压力数值。

然而,有些传感器需要返回多个数值。比如,KX022-1020 加速度计会测量三个轴上的减速度:X、Y和Z。显然,我们需要使用一个数组,以返回所有数值。数组所用内存是动态分配的,所以当我们不需要数组时,我们必须重新释放内存。

在下面的例子中,我们定义、包含、实例化并启动KX022-1020加速度计。然后,我们创建一个名为accelValue的浮点数动态数组。如果我们后面不再需要数组,我们可以通过delete[]释放内存。这样可以确保没有内存泄漏。内存泄漏是指动态分配的内存无法正确释放,并且只要Arduino未重置就不可访问的情况。在这个例子中,您会看到如何正确释放动态分配的内存。

#define INCLUDE_KX022_1020
 
#include <RohmMultiSensor.h>
 
 
 
KX022_1020 acc;
 
 
 
void setup() {
 
 Serial.begin(9600);
 
 Wire.begin();
 
 
 
 acc.init();
 
 
 
 Serial.println("X[g]\tY[g]\tZ[g]”);
 
}
 
 
 
void loop() {
 
 // dynamically create an array
 
 float* accelValue = acc.measure();
 
 
 
 // now we can access the elements in accelValue array
 
 Serial.print(accelValue[0]);
 
 Serial.print('\t');
 
 Serial.print(accelValue[1]);
 
 Serial.print('\t');
 
 Serial.println(accelValue[2]);
 
 
 
 //safely deallocate the memory
 
 delete[] accelValue;
 
 
 
 delay(100);
 
}

中断和BM1422GMV地磁传感器

如果您用过Arduino,您很可能已经见过中断功能了。即使没有,也请不要担心,如果设置正确,中断用起来很简单。

简单来讲,中断就是让Arduino“跳转”至代码特定部分的信号。这部分代码是一种称为中断服务程序的特殊函数,通常简称为ISR。中断的最常见用途就是进行精确时序控制,所以ISR函数应尽可能短。此外,中断函数不带任何参数,也不会返回任何值。

让我们来看一下需要正确设置中断的BM1422GMV地磁传感器。

#define INCLUDE_BM1422GMV
 
 
 
#include <RohmMultiSensor.h>
 
 
 
BM1422GMV mag;
 
 
 
void isr(void) {
 
 mag.setFlagDrdy();
 
}
 
 
 
void setup() {
 
 Serial.begin(9600);
 
 Wire.begin();
 
 
 
 mag.init(isr);
 
 
 
 Serial.println("X[uT]\tY[uT]\tZ[uT]");
 
}
 
 
 
void loop() {
 
 float* magValue = mag.measure();
 
 
 
 Serial.print(magValue[0]);
 
 Serial.print('\t');
 
 Serial.print(magValue[1]);
 
 Serial.print('\t');
 
 Serial.println(magValue[2]);
 
 
 
 delete[] magValue;
 
 
 
 delay(100);
 
}

我们需要内存释放(因为与加速度计类似,BM1422GMV会返回三个轴的数值)以及ISR。您可以看到,中断只是在BM1422GMV类中形成一个标志,表示有新数据需要收集。.setFlagDrdy()方法是该库的一部分。请注意,您必须向.init()方法提供ISR名称。

然而,只在代码中设置中断服务程序是不够的。我们需要在扩展板上选择正确设置。

Arduino UNO具有两个外部中断:INT0(引脚D2)和 INT1(引脚D3)。我们必须将适当的引脚连到传感器上的INT引脚。您可以在类的实例化中选择使用哪个中断。使用Arduino引脚D2中断的默认值为INT_0,或者如果Arduino D2上的中断已经用于其他设置,您可以将其改为INT_1。

arduino library

图 5. 负责中断设置的所有引脚概览

要将传感器中断连至Arduino,我们应使用J3和J4排针。J3将中断连至Arduino D2,而J4则将中断连至D3。每个排针都有标记为INT1至INT5的引脚以及标记为INTR1至INTR5的引脚。这是因为传感器具有两种不同的中断输出:

  1. CMOS类型输出:KX022-1020和BM1422GMV属于这种输出。这些中断信号可以直接连至 Arduino,并且使用引脚INTR1-5。
  2. 需要外部上拉电阻的中断:使用这种中断的传感器为BH1745NUC和RPR-0521RS。它们会使用引脚INT1-5。注:在排针J16上,相应的引脚号必须短接,以使能外部上拉电阻。比如,如果要在J3上使用INT1引脚,您还必须将INT1短接至J16。

我们再看一下上文给出的示例代码。我们想要把Arduino的INT0连至BM1422GMV的INT引脚。假设传感器连到了I2C_1插槽。在这种情况下,我们应将INTR1引脚短接至J3。

arduino library

图 6. BM1422GMV连至I2C_1插槽时的中断设置

 

如果任何其他传感器需要使用中断——比如BH1745NUC颜色传感器,那么中断设置步骤如下所示:如果我们将传感器连至I2C_3并且要使用Arduino的中断INT1,那么我们应将引脚INT3短接到J4和J16上,因为该传感器需要一个外部上拉电阻。

arduino library

图 7. H1745NUC连至I2C_3插槽时的中断设置

 

有关中断的详细信息以及针对不同传感器的设置信息,请参考该库README中的注释部分。

技巧和决窍

1.地磁传感器(BM1422GMV)是唯一需要中断才能工作的传感器。然而,所有其他I2C传感器也可以使用中断。库的运行不需要使能中断,但是这在一些应用程序中可能非常有用。目前,除磁力计之外,库中没有实现其他传感器的中断功能。

2.使用霍尔(BD7411G)传感器时,向Arduino上传程序之前必须断开传感器。否则,您将在上传程序时收到以下错误信息:avrdude: stk500_getsync() 错误。这是因为BD7411G上的OUT引脚直接连至Arduino引脚D0,而D0还是串行RX引脚。如果没有检测到磁场,那么BD7411G的输出为高电平(HIGH),这会阻止串行端口的任何传入通信——包括程序上传。

 

arduino library

图8.使用BD7411G时可能遇到的错误示例。请注意,如果您在Arduino IDE设置中关闭了详细输出,您将只会收到一条消息:“上传程序时出现错误(An error occurred while uploading the sketch)”。要打开详细输出信息,请转到Arduino IDE ->文件(File)->首选项(Preferences),然后勾选“上传时显示详细输出(Show verbose output during upload)”。

3.当使用其中一个模拟传感器(BD1020HFV温度传感器或ML8511A UV传感器)时,您必须提供传感器所连接的插槽名称。扩展板上有两个模拟插槽:ANALOG_1和ANALOG_2。为其中一个模拟传感器调用.init()方法时,您必须向.init()方法提供插槽名称(即调用.init(ANALOG_1),以初始化连至插槽ANALOG_1的模拟传感器)。作为安全措施,如果您没有提供任何内容,.init()的默认值将会是 ANALOG_1,然而,传感器必须连至插槽ANALOG_1。如果您从模拟传感器获取的数据很奇怪,那么请首先确保您提供的插槽名称与传感器相连的插槽一致。

本文的主要目的是为罗姆源代码提供一种潜在的替代方案。顾名思义,如果您需要在罗姆传感器评估套件中处理多个传感器,那么罗姆MultiSensor库将会非常有用——您的下一个大型项目可能会需要这个库!

如果您有任何改进建议,请GitHub上进行分享:创建修改意见;或者在问题(Issues) 选项卡中建立GitHub问题。创建GitHub问题非常简单,您的反馈对我们来说非常有价值。另外,如果您喜欢这个库,如果您的代码由此变得更漂亮,那么请考虑赏给罗姆MultiSensor程序库一颗星。尽情开发吧!

DevicePlus 编辑团队
Jan Gromes

Jan目前在布尔诺理工大学学习电气工程。他拥有多年使用Arduino和其他微控制器构建项目的经验。他的特殊兴趣在于机器人系统的机械设计。

分享到社交媒体