欢迎光临
我们一直在努力

LabVIEW与Arduino如何进行联合编程?(1)

沛华LabVIEW基础课程|第2讲

『LabVIEW与Arduino如何进行联合编程?』(1)——安装LINX工具包

上一期已经跟大家讲了(点击蓝字回顾),但是,装完后看着手里的Arduino是不是一脸懵啊,那么,该如何使用LabVIEW与Arduino进行联合编程呢?

其实,通过我们这次安装的LINX工具包,可以实现的是LabVIEW跟Arduino之间的串口通信。通过串口通信,LabVIEW发送相关指令给Arduino,Arduino执行相应动作或返回相应的数据给LabVIEW,通常在这里我们称LabVIEW所在的PC为上位机,Arduino硬件为下位机。

说起来这么简单,没有相应的代码怎么行呢?试想一下,如果没有LabVIEW上位机程序与相应的Arduino下位机程序,LabVIEW跟Arduino就像两个植物人一样没有了逻辑思考能力,他们压根无法进行沟通。

如果单纯只有LabVIEW上位机程序,则像一个正常人跟一个植物人说话一样,正常人说的话(发出的相应指令)基本就是石沉大海没有一点回应;如果单纯的只有Arduino下位机程序也一样,这么说是不是同时拥有LabVIEW程序和Arduino的程序就可以了?

那也不行啊!试想一下,如果有两个只会本国语言的Human being,一个讲日文,一个讲英文,你说你的,我说我的,是无法进行有效沟通的,同样道理,LabVIEW程序如果与Arduino程序没有“对应”起来,没有“约定好”相对应的通信方式与相应的数据格式,那么它们是通信不起来的哦,那谈何上下位机通信呀!

So,PC上得跑LabVIEW写的上位机程序,Arduino硬件得跑相应的C语言下位机程序,听起来是不是好麻烦好想放弃,真的是还没入门就想放弃哈哈!

但是,看完后面的教程,你将Don’t Worry Anymore! LINX工具包已经帮我们打包好相应的LabVIEW程序了,它把那些复杂的底层通信程序封装成了一个个简单易用的子VI,供我们调用,Arduino的程序也不用我们花时间去写去Debug呀,爽歪歪有没有,直接在一开始的时候烧录到Arduino进去就可以了,想想就好激动!

向上滑动查看LINX-Arduino部分源代码

/****************************************************************************************

**  LINX - Wiring compatible device code

**  For more information see:          

**  For support visit the forums at:    

**  Written By Sam Kristoff

** BSD2 License.

****************************************************************************************/

/****************************************************************************************

**  Defines

****************************************************************************************/

/****************************************************************************************

**  Includes

****************************************************************************************/

#include "LinxDevice.h"

#include "LinxWiringDevice.h"

#if ARDUINO_VERSION >= 100

#include <Arduino.h>

#include <WProgram.h>

//Not all wiring devices have these...

#ifndef EXTERNAL

#define EXTERNAL 0

#ifndef DEFAULT

#define DEFAULT 0

#include <SPI.h>

#include <Wire.h>

#include <EEPROM.h>

#include <Servo.h>

/****************************************************************************************

**  Variables

****************************************************************************************/

/****************************************************************************************

**  Constructors / Destructors

****************************************************************************************/

LinxWiringDevice::LinxWiringDevice()

//LINX API Version

LinxApiMajor = 3;

LinxApiMinor = 0;

LinxApiSubminor = 0;

//Load User Config Data From Non Volatile Storage

userId = NonVolatileRead(NVS_USERID) << 8 | NonVolatileRead(NVS_USERID + 1);

/****************************************************************************************

**  Functions

****************************************************************************************/

void LinxWiringDevice::DelayMs(unsigned long ms)

unsigned long LinxWiringDevice::GetMilliSeconds()

return millis();

unsigned long LinxWiringDevice::GetSeconds()

return (millis() / 1000);

//--------------------------------------------------------ANALOG-------------------------------------------------------

int LinxWiringDevice::AnalogRead(unsigned char numChans, unsigned char* channels, unsigned char* values)

unsigned int analogValue = 0;

unsigned char responseByteOffset = 0;

unsigned char responseBitsRemaining = 8;

unsigned char dataBitsRemaining = AiResolution;

values[responseByteOffset] = 0x00;    //Clear FirstResponse Byte

//Loop Over All AI channels In Command Packet

for(int i=0; i<numChans; i++)

analogValue = analogRead(channels[i]);

dataBitsRemaining = AiResolution;

//Byte Packet AI Values In Response Packet

while(dataBitsRemaining > 0)

*(values+responseByteOffset) |= ( (analogValue>>(AiResolution - dataBitsRemaining)) << (8 - responseBitsRemaining) );

//*(values+responseByteOffset) = 69;

if(responseBitsRemaining > dataBitsRemaining)

//Current Byte Still Has Empty Bits

responseBitsRemaining -= dataBitsRemaining;

dataBitsRemaining = 0;

//Current Byte Full

dataBitsRemaining = dataBitsRemaining - responseBitsRemaining;

responseByteOffset++;

responseBitsRemaining = 8;

values[responseByteOffset] = 0x00;    //Clear Next Response Byte

return L_OK;

int LinxWiringDevice::AnalogSetRef(unsigned char mode, unsigned long voltage)

switch(mode)

case 0: //Default

analogReference(DEFAULT);

AiRefSet = AiRefDefault;

case 1: //Internal

if(NumAiRefIntVals > 0)

//Check If Internal AI Ref Value Is Supported

for(int i=0; i<NumAiRefIntVals; i++)

//Voltage Is Supported

if(AiRefIntVals[i] == voltage)

analogReference(AiRefCodes[i]);

AiRefSet = voltage;

return L_OK;

//Didn't Find Voltage

return LANALOG_REF_VAL_ERROR;

//No Internal Voltages, So Internal Mode Not Supported

return LANALOG_REF_MODE_ERROR;

case 2: //External

if(voltage >= AiRefExtMin && voltage <= AiRefExtMax)

analogReference(EXTERNAL);

AiRefSet = voltage;

return L_OK;

return LANALOG_REF_VAL_ERROR;

return LANALOG_REF_MODE_ERROR;

return L_OK;

//--------------------------------------------------------DIGITAL-------------------------------------------------------

int LinxWiringDevice::DigitalWrite(unsigned char numChans, unsigned char* channels, unsigned char* values)

for(int i=0; i<numChans; i++)

pinMode(channels[i], OUTPUT);

digitalWrite( channels[i], (values[i/8] >> i%8) & 0x01);

return L_OK;

int LinxWiringDevice::DigitalRead(unsigned char numChans, unsigned char* channels, unsigned char* values)

unsigned char bitOffset = 8;

unsigned char byteOffset = 0;

unsigned char retVal = 0;

//Loop Over channels To Read

for(int i=0; i<numChans; i++)

//If bitOffset Is 0 We Have To Start A New Byte, Store Old Byte And Increment OFfsets

if(bitOffset == 0)

//Insert retVal Into Response Buffer

values[byteOffset] = retVal;

retVal = 0x00;

byteOffset++;

bitOffset = 7;

bitOffset--;

//Read From Next Pin

unsigned char pinNumber = channels[i];

pinMode(pinNumber, INPUT);//Set Pin As Input (Might Make This Configurable)

retVal = retVal | (digitalRead(pinNumber) << bitOffset);//Read Pin And Insert Value Into retVal

//Store Last Byte

values[byteOffset] = retVal;

return L_OK;

int LinxWiringDevice::DigitalWriteSquareWave(unsigned char channel, unsigned long freq, unsigned long duration)

if(freq > 0)

pinMode(channel, OUTPUT);

if(duration > 0)

tone(channel, freq, duration);

tone(channel, freq);

noTone(channel);

return L_OK;

int LinxWiringDevice::DigitalReadPulseWidth(unsigned char stimChan, unsigned char stimType, unsigned char respChan, unsigned char respType, unsigned long timeout, unsigned long* width)

if(stimType == 1)

//High->Low->High

pinMode(stimChan, OUTPUT);

digitalWrite(stimChan, HIGH);

digitalWrite(stimChan, LOW);

digitalWrite(stimChan, HIGH);

else if(stimType == 2)

//Low->High->Low

pinMode(stimChan, OUTPUT);

digitalWrite(stimChan, LOW);

digitalWrite(stimChan, HIGH);

digitalWrite(stimChan, LOW);

//Read Pulse

pinMode(respChan, INPUT);

if(respType == 0)

*width = pulseIn(respChan, LOW, timeout);

else if(respType == 1)

*width = pulseIn(respChan, HIGH, timeout);

return L_OK;

//--------------------------------------------------------PWM-----------------------------------------------------------

int LinxWiringDevice::PwmSetDutyCycle(unsigned char numChans, unsigned char* channels, unsigned char* values)

for(int i=0; i<numChans; i++)

pinMode(channels[i], OUTPUT);

analogWrite(channels[i], values[i]);

return L_OK;

//--------------------------------------------------------SPI-----------------------------------------------------------

int LinxWiringDevice::SpiOpenMaster(unsigned char channel)

SPI.begin();

int LinxWiringDevice::SpiSetBitOrder(unsigned char channel, unsigned char bitOrder)

SPI.setBitOrder(bitOrder);

int LinxWiringDevice::SpiSetMode(unsigned char channel, unsigned char mode)

switch(mode)

SPI.setDataMode(SPI_MODE0);

SPI.setDataMode(SPI_MODE1);

SPI.setDataMode(SPI_MODE2);

SPI.setDataMode(SPI_MODE3);

int LinxWiringDevice::SpiSetSpeed(unsigned char channel, unsigned long speed, unsigned long* actualSpeed)

//Loop Over All Supported SPI Speeds (SPI Speeds Should Be Fastest -> Slowest)

for(int index=0; index < NumSpiSpeeds; index++)

//If Target Speed Is greater or equal to the current supported speed use current supported speed (it's the fastest supported speed that is less or equal to the target)

if(speed >= *(SpiSupportedSpeeds+index))

*actualSpeed = *(SpiSupportedSpeeds+index);

SPI.setClockDivider(*(SpiSpeedCodes+index));

if(index == NumSpiSpeeds-1)

//Target speed is slower than slowest supported.  Use slowest supported

*actualSpeed = *(SpiSupportedSpeeds+index);

SPI.setClockDivider(*(SpiSpeedCodes+index));

return L_OK;

int LinxWiringDevice::SpiWriteRead(unsigned char channel, unsigned char frameSize, unsigned char numFrames, unsigned char csChan, unsigned char csLL, unsigned char* sendBuffer, unsigned char* recBuffer)

//Set CS Pin As DO

pinMode(csChan, OUTPUT);

//Set CS Pin Idle Before Starting SPI Transfer

digitalWrite(csChan, (~csLL & 0x01) );

//Loop Over Frames

for(int i=0; i<numFrames; i++)

//Start of frame, set CS Pin Active

digitalWrite(csChan, (csLL & 0x01) );

//Loop Over Bytes In Frame

for(int j=0; j<frameSize; j++)

//Transfer Data

unsigned char byteNum = (i*frameSize) + j;

recBuffer[byteNum] = SPI.transfer(sendBuffer[byteNum]);

//Frame Complete, Set CS idel

digitalWrite(csChan, (~csLL & 0x01) );

//--------------------------------------------------------I2C-----------------------------------------------------------

//Helper To Deal With Arduino API Changes

void LinxWireWrite(unsigned char data)

#if ARDUINO_VERSION < 100

Wire.send(data);

Wire.write(data);

int LinxWiringDevice::I2cOpenMaster(unsigned char channel)

if(*(I2cRefCount+channel) > 0)

//Channel Already Open, Increment Ref Count

*(I2cRefCount+channel) = *(I2cRefCount+channel)+1;

//Channel Not Yet Open, Open And Set Refcount = 1

Wire.begin();//TODO ONLY SUPPORT ONE CHANNEL ATM

int LinxWiringDevice::I2cSetSpeed(unsigned char channel, unsigned long speed, unsigned long* actualSpeed)

return L_OK;

int LinxWiringDevice::I2cWrite(unsigned char channel, unsigned char slaveAddress, unsigned char eofConfig, unsigned char numBytes, unsigned char* sendBuffer)

#if ARDUINO_VERSION >= 100

Wire.beginTransmission(slaveAddress);

Wire.write(sendBuffer, numBytes);

if(eofConfig == EOF_STOP)

Wire.endTransmission(true);

else if (eofConfig == EOF_RESTART)

Wire.endTransmission(false);

//EOF Not Supported, Stop Bus

Wire.endTransmission(true);

return LI2C_EOF;

return L_OK;

if(eofConfig != EOF_STOP)

//EOF Not Supported, Stop Bus

return LI2C_EOF;

Wire.beginTransmission(slaveAddress);

for(int i=0; i<numBytes; i++)

Wire.send(*(sendBuffer+i));

Wire.endTransmission();

int LinxWiringDevice::I2cRead(unsigned char channel, unsigned char slaveAddress, unsigned char eofConfig, unsigned char numBytes, unsigned int timeout, unsigned char* recBuffer)

#if ARDUINO_VERSION >= 100

if(eofConfig == EOF_STOP)

Wire.requestFrom(slaveAddress, numBytes, (uint8_t)1);

else if (eofConfig == EOF_RESTART)

Wire.requestFrom(slaveAddress, numBytes, (uint8_t)0);

//EOF Not Supported

return LI2C_EOF;

if(eofConfig != EOF_STOP)

//EOF Not Supported

return LI2C_EOF;

Wire.requestFrom(slaveAddress, (uint8_t)numBytes);

//Wait For Data, Potentially Timeout

unsigned long tickCount = millis();

while(Wire.available() < numBytes)

if( (millis() - tickCount) > timeout)

return LI2C_READ_FAIL;

//Data Read, Read And Return

for(int i=0; i<numBytes; i++)

#if ARDUINO_VERSION >= 100

*(recBuffer+i) = Wire.read();

*(recBuffer+i) = Wire.receive();

return L_OK;

int LinxWiringDevice::I2cClose(unsigned char channel)

//Function Not Supported

return L_FUNCTION_NOT_SUPPORTED;

//--------------------------------------------------------UART----------------------------------------------------------

int LinxWiringDevice::UartOpen(unsigned char channel, unsigned long baudRate, unsigned long* actualBaud)

int index = 0;

for(index=0; index < NumUartSpeeds; index++)

if(baudRate < *(UartSupportedSpeeds+index))

//Previous Index Was Closest Supported Baud Without Going Over, Index Will Be Decremented Accordingly Below.

//Once Loop Complets Index Is One Higher Than The Correct Baud, But Could Be Zero So Check And Decrement Accordingly

//If The Entire Loop Runs Then index == NumUartSpeeds So Decrement It To Get Max Baud

if(index != 0)

index = index -1;

if(channel == 0)

#if NUM_UART_CHANS > 0

Serial.begin(*(UartSupportedSpeeds+index));

*actualBaud = *(UartSupportedSpeeds+index);

if(channel == 1)

#if NUM_UART_CHANS > 1

Serial1.begin(*(UartSupportedSpeeds+index));

*actualBaud = *(UartSupportedSpeeds+index);

if(channel == 2)

#if NUM_UART_CHANS > 2

Serial2.begin(*(UartSupportedSpeeds+index));

*actualBaud = *(UartSupportedSpeeds+index);

if(channel == 3)

#if NUM_UART_CHANS > 3

Serial3.begin(*(UartSupportedSpeeds+index));

*actualBaud = *(UartSupportedSpeeds+index);

return L_OK;

int LinxWiringDevice::UartSetBaudRate(unsigned char channel, unsigned long baudRate, unsigned long* actualBaud)

UartClose(channel);

int retVal = UartOpen(channel, baudRate, actualBaud);

return retVal;

int LinxWiringDevice::UartGetBytesAvailable(unsigned char channel, unsigned char *numBytes)

if(channel == 0)

#if NUM_UART_CHANS > 0

*numBytes = Serial.available();

if(channel == 1)

#if NUM_UART_CHANS > 1

*numBytes = Serial1.available();

if(channel == 2)

#if NUM_UART_CHANS > 2

*numBytes = Serial2.available();

if(channel == 3)

#if NUM_UART_CHANS > 3

*numBytes = Serial3.available();

return L_OK;

int LinxWiringDevice::UartRead(unsigned char channel, unsigned char numBytes, unsigned char* recBuffer, unsigned char* numBytesRead)

#if ARDUINO_VERSION >= 100

if(channel == 0)

#if NUM_UART_CHANS > 0

*numBytesRead = Serial.readBytes((char*)recBuffer, numBytes);

else if(channel == 1)

#if NUM_UART_CHANS > 1

*numBytesRead = Serial1.readBytes((char*)recBuffer, numBytes);

else if(channel == 2)

#if NUM_UART_CHANS > 2

*numBytesRead = Serial2.readBytes((char*)recBuffer, numBytes);

else if(channel == 3)

#if NUM_UART_CHANS > 3

*numBytesRead = Serial3.readBytes((char*)recBuffer, numBytes);

if(*numBytesRead !=numBytes)

return LUART_READ_FAIL;

return L_OK;

for(int i=0; i<numBytes; i++)

int data = -1;

if(channel == 0)

#if NUM_UART_CHANS > 0

data = Serial.read();

else if(channel == 1)

#if NUM_UART_CHANS > 1

data = Serial1.read();

else if(channel == 2)

#if NUM_UART_CHANS > 2

data = Serial2.read();

else if(channel == 3)

#if NUM_UART_CHANS > 3

data = Serial3.read();

if(data < 0)

return LUART_READ_FAIL;

*(recBuffer+i) = (char)data;

//Read All Bytes Without Error.  Return Num Bytes Read So Listener Can Pass It To PacketizeAndSend()

*numBytesRead = numBytes;

return L_OK;

int LinxWiringDevice::UartWrite(unsigned char channel, unsigned char numBytes, unsigned char* sendBuffer)

if(channel == 0)

#if NUM_UART_CHANS > 0

Serial.write(sendBuffer, numBytes);

if(channel == 1)

#if NUM_UART_CHANS > 1

Serial1.write(sendBuffer, numBytes);

if(channel == 2)

#if NUM_UART_CHANS > 2

Serial2.write(sendBuffer, numBytes);

if(channel == 3)

#if NUM_UART_CHANS > 3

Serial3.write(sendBuffer, numBytes);

return L_OK;

int LinxWiringDevice::UartClose(unsigned char channel)

if(channel == 0)

#if NUM_UART_CHANS > 0

Serial.end();

if(channel == 1)

#if NUM_UART_CHANS > 1

Serial1.end();

if(channel == 2)

#if NUM_UART_CHANS > 2

Serial2.end();

if(channel == 3)

#if NUM_UART_CHANS > 3

Serial3.end();

return L_OK;

//--------------------------------------------------------SERVO----------------------------------------------------------

int LinxWiringDevice::ServoOpen(unsigned char numChans, unsigned char* chans)

for(int i=0; i<numChans; i++)

unsigned char pin = chans[i];

if(Servos[pin] == 0)

//Servo Not Yet Intialized On Specified Channel, Init

Servos[pin] = new Servo();

Servos[pin]->attach(pin);

DebugPrint("Created New Servo On Channel ");

DebugPrintln(pin, DEC);

return L_OK;

int LinxWiringDevice::ServoSetPulseWidth(unsigned char numChans, unsigned char* chans, unsigned short* pulseWidths)

for(int i=0; i<numChans; i++)

DebugPrint("Servo ");

DebugPrint((unsigned long)Servos[chans[i]], DEC);

DebugPrint(" : ");

DebugPrintln(pulseWidths[i], DEC);

Servos[chans[i]]->writeMicroseconds(pulseWidths[i]);

return L_OK;

int LinxWiringDevice::ServoClose(unsigned char numChans, unsigned char* chans)

for(int i=0; i<numChans; i++)

Servos[chans[i]]->detach();

Servos[chans[i]] = 0;

return L_OK;

//--------------------------------------------------------GENERAL----------------------------------------------------------

void LinxWiringDevice::NonVolatileWrite(int address, unsigned char data)

EEPROM.write(address, data);

unsigned char LinxWiringDevice::NonVolatileRead(int address)

return EEPROM.read(address);

如果都要自己写是不是很绝望?

有了LINX工具包,你就是LabVIEW爸爸,Arduino就是你的儿子,你们通过“说话(串口)”进行通信,你发指令给Arduino儿子,要他干啥就干啥,要他给你返回啥数据就返回啥数据,满满的控制欲,不过前提得吩咐Arduino做他力所能及的事啊。

为了马上成为发号司令的LabVIEW爸爸,接下来就马上来安装Digilent公司(NI旗下子公司)写的LINX工具包啦。

安装完LabVIEW的同学应该会发现:你的电脑顺带也安装了VI Package Manager,如下图:

诺,就是右边这货

没有自动创建快捷方式的同学可以去Windows的“开始—搜索”搜索一下。

VI Package Manager能下载很多实用的工具包,为LabVIEW的编程带来了极大的便利,不过有些工具包需要付费才可以使用。我们这里下载的LINX工具包是免费的。

打开VI Package Manager(下面简称VIPM),加载时间会点长,大概1-2分钟。

打开后选择电脑LabVIEW对应的版本,我装的是LabVIEW 2014,所以选择的是“2014”,在搜索框输入“linx”,可以发现一个Digilent LINX工具包,这个就是我们要的工具包。

鼠标双击打开工具包,确认一遍是不是我们要的版本,确认无误后点击“Install”安装。

选择“Continue”继续。

选择同意安装。

等一小会,大概1-2分钟。

出现“installed /No Errors”则表示安装成功,选择“Finish”完成安装。

安装过程中VIPM会自动与LabVIEW进行连接通信,如果连接失败,LINX工具包安装就会失败,那我们就要自己设置一下LabVIEW的相关配置:

设置方法:

展开LabVIEW的“Tools(工具)”菜单,找到“Options(选项)”,点击进入,如下图:

找到“VI Server”(VI服务器),点击“Add”添加一个“*”符号,表示所有机器都可以访问,点击OK按钮完成设置;

再次打开VIPM,在菜单栏找到“Edit”,点击“Configure LabVIEW Versions”。

点击“Verify”,验证成功的话“Connection Verified”那里会显示“Yes”。

最后点击“OK”就完成啦,重复上面的步骤就可以完成LINX的安装。

打开LabVIEW,在程序框图能够找到”MakerHub-LINX“,说明LINX安装成功啦!

1.这个LINX通信程序等你有一定基础的时候可以尝试自己写,自己根据具体项目写的更具有针对性,效率会更高,这也是自我提升的一种方法;

2.如果你想部署LabVIEW的代码到Arduino里,就得使用Arduino™ Compatible Compiler for LabVIEW这个工具包,与LINX工具包不同的是,它具有能把LabVIEW代码编译成Arduino代码的编译器。这个工具包也可以通过VIPM安装,不过是要付费的哦,还好有7天的免费试用期,等我们后面深入学习一段时间也可以来玩一玩,体验一下用LabVIEW开发Arduino的乐趣;

3.要是再牛掰的同学,你发现有什么功能大家经常要用,也可以做成工具包挂到VI Package Manager,没准能赚到人生的第一桶金……

咳咳咳,又扯远了。今天就到这啦,我是Shania,下期再会~

比你想象的更好玩!

写于:18.12.03

赞(36)
分享到: 更多

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址