You are on page 1of 97

Programming Arduino-based Systems

in C#
A Practical Guide

By Yury Magda

Copyright 2017 by Yury Magda. All rights


reserved.

The programs, examples, and applications presented in


this book have been included for their instructional value.
The author offers no warranty implied or express,
including but not limited to implied warranties of fitness
or merchantability for any particular purpose and do not
accept any liability for any loss or damage arising from
the use of any information in this book, or any error or
omission in such information, or any incorrect use of
these programs, procedures, and applications.
No part of this publication may be reproduced, stored in a
retrieval system, or transmitted in any form or by any
means, electronic, mechanical, photocopying, recording,
or otherwise, without the prior written permission of the
author.

2
Contents
Introduction
Disclaimer
Using the FT4232H device in measurement systems
Software
Hardware
Example 1
Example 2
Example 3
Example 4
Example 5
Example 6
Example 7
Example 8
Example 9
Example 10

3
Introduction
This book is written for hobbyists and developers who would like to be
able to program Arduino-based measurement and control systems in C#
.NET.

The hardware used in designs from this book consists of the high-
performance USB interface module equipped with the FT4232H device
from FTDI and the Arduino Zero board. The material of the book assumes
that the readers are familiar, at least, with basics of designing and
assembling electronic circuits. For most projects having some basic skills
in electronics will serve the readers well and allow them to understand
what is going on behind the scenes.

The software of the systems is written in Visual C# .NET 2017


Community Edition installed on the PC running Windows 10. The source
code of applications may also be compiled in earlier versions of Visual C#
.NET running in Windows 8 or 10. Each project is accompanied by a
detailed description of hardware and software which helps to make things
clear. All projects described in this guide can be easily improved or
modified if necessary.

Disclaimer
The design techniques described in this book have been tested using the
Arduino Zero board and FT4232H Breakout module from Numato Lab
(https://numato.com) without damage of the equipment. I will not accept
any responsibility for damages of any kind due to actions taken by you
after reading this book.

Using the FT4232H device in measurement


systems
Using the FT4232H chip allows to extend capabilities of the systems based

4
upon microcontrollers. This is due to the following features:
Single chip USB to quad serial ports with a variety of configurations;
USB 2.0 High Speed (480Mbits/Second) and Full Speed
(12Mbits/Second) compatible;
Two Multi-Protocol Synchronous Serial Engine (MPSSE) on
CHANNEL A and CHANNEL B to simplify synchronous serial
protocol (USB to JTAG, I2C, SPI or bit-bang) design;
Entire USB protocol handled on the chip. No USB specific firmware
programming required;
RS232/RS422/RS485 UART transfers up to 12M baud;
FTDI's royalty-free Virtual Com Port (VCP) and Direct (D2XX)
drivers eliminate the requirement for USB driver development in
most cases.

When designing embedded systems, a developer can use the various


configurations and interfaces. A few possible configurations are listed
below:

Single chip USB to four channel UART (RS232, RS422 or RS485)


or Bit-Bang interfaces
Single chip USB to 2 JTAG channel plus 2 UARTS
Single chip USB to 2 SPI channel plus 2 UARTS
Single chip USB to 2 I2C channel plus 2 UARTS
Single chip USB to 2 Bit-Bang channel plus 2 UARTS

With the FT4232H device a developer can directly control microcontroller


boards via digital inputs/outputs, SPI/I2C interfaces and UARTs using
high-level applications written in C++, C# .NET or other languages.
Additionally, we can get a lot of additional digital inputs/outputs on
FT4232H device controlled by a microcontroller board.

In this guide, we will see how to build hardware/software interfaces


between the FT4232H device and popular Arduino Zero board. The
Arduino Zero and similar boards (Arduino M0, Adafruit Feather M0, etc.)
use the Atmel's SAMD21G18 microcontroller featuring a 32-bit ARM
Cortex M0 core that offers excellent potential to create most imaginative
and new ideas for high tech automation. Additionally, these Arduino
boards operates with signal levels of 3.3V that allows to directly connect
I/O pins to FT4232H.
Note that projects from this guide can be adopted to other microcontroller

5
boards with ARM Cortex microcontrollers with corresponding changes in
hardware/software.

Software

In this guide, all program code relating to FT4232H is written in C# .NET


using Visual Studio 2017 Community Edition. When designing projects
with the FT4232H chip, we will use the software resources on
www.ftdi.com. First, we need to install D2XX drivers
(http://www.ftdichip.com/Drivers/D2XX.htm). The drivers allow direct
access to the USB device through a DLL. Application software can access
the USB device through a series of DLL function calls.
The functions available are listed in the D2XX Programmer's Guide (
http://www.ftdichip.com/Support/Documents/ProgramGuides/D2XX_Programmer's_G
which is available from the section
(http://www.ftdichip.com/Support/FTDocuments.htm).

For better understanding the subject, you can also view the programming
examples for C# .NET where the D2XX drivers and DLLs are used. That
stuff can be found in the section of the FTDI site:

http://www.ftdichip.com/Support/SoftwareExamples/CodeExamples/CSharp.htm

This page contains examples of communicating with FTDI devices


through the D2XX drivers and FTD2XX.DLL using C# .NET.
FTDI have provided a managed .NET wrapper class for the FTD2XX DLL
on the Windows platform that is available at
http://www.ftdichip.com/Support/SoftwareExamples/CodeExamples/CSharp/FTD2XX_
The managed wrapper DLL (FTD2XX_NET.DLL) is provided as a free
download with Intellisense documentation provided in the
FTD2XX_NET.XML file - this is viewable in the Visual Studio Object
Browser and also provides hints as the code is written.

All our projects will use FTD2XX_NET.dll library so we should put the
files

FTD2XX_NET.dll
FTD2XX_NET.XML

6
into the working directory of each C# .NET project.

With FT4232 we are capable to realize various types of fast interfaces by


writing a C# .NET code as well as various data processing algorithms.

Hardware

There are a lot of interface boards equipped with the high-performance 4-


channel FT4232H chip from FTDI. All projects from this guide are based
upon the FT4232H Breakout board manufactured by Numato Lab so the
brief description of the board is taken from their site www.numato.com. Of
course, you can take other FT4232H boards for experiments and use the
software described in this book with proper modifications. Note that you
must carefully consider the pin assignments for the FT4232H boards
from other manufactures and make corresponding changes in a C#
source code.

Numato Lab's FT4232H Breakout (Fig.1) module is a versatile product for


USB to multi-port JTAG, SPI and I2C interfaces (Two Multi-Protocol
Synchronous Serial Engines available with the FT4232H) and USB to
multi-port asynchronous serial interfaces.

Fig.1

The pin assignments for the FT4232H Breakout is shown in the table
below.

7
Channel A Channel B Channel C Channel D
Pin Pin Name Pin Pin Name Pin Pin Pin
Name
P1.1 GND P1.13 BDBUS1 P2.13 GND P2.1
P1.2 VCC P1.14 BDBUS0 P2.14 GND P2.2
P1.3 ADBUS1 P1.15 BDBUS3 P2.15 CDBUS7 P2.3
P1.4 ADBUS0 P1.16 BDBUS2 P2.16 CDBUS6 P2.4
P1.5 ADBUS3 P1.17 BDBUS5 P2.17 CDBUS5 P2.5
P1.6 ADBUS2 P1.18 BDBUS4 P2.18 CDBUS4 P2.6
P1.7 ADBUS5 P1.19 BDBUS7 P2.19 CDBUS3 P2.7
P1.8 ADBUS4 P1.20 BDBUS6 P2.20 CDBUS2 P2.8
P1.9 ADBUS7 P1.21 SUSPEND P2.21 CDBUS1 P2.9
P1.10 ADBUS6 P1.22 GND P2.22 CDBUS0 P2.10
P1.11 RESET P1.23 GND P2.23 GND P2.11
P1.12 GND P1.24 VCC P2.24 VCC P2.12

Alternatively, you can also see the user manual on the FT4232H Breakout.
The block diagram that illustrates interfacing the FT4232H board and
Arduino Zero is shown in Fig.2.

8
Fig.2

The following chapters describe practical examples of hardware/software


interfacing FT4232H and Arduino.

Example 1
This example illustrates how to implement simple control of the Arduino
Zero through an external interrupt. Triggering the interrupt will be
performed by a Windows application written in C# .NET.
The connections between the FT4232H and Arduino Zero boards are
shown in Fig.3.

9
Fig.3

In this circuit, the rising/falling edge on pin 4 of the FT4232H board


triggers the external interrupt on pin 8 of the Arduino Zero. The Interrupt
Service routine (ISR) associated with this interrupt causes the on-board
LED (pin 13 of Arduino) to be toggled each time the interrupt occurs. In
this system, we need to develop the program code for both Arduino Zero
and FT4232H.
Lets discuss the program code developed for FT4232H first. The
application for FT4232H will be created using the Project Wizard of the
Visual Studio IDE. Select the Visual C# option under Templates, then
select the type Windows Form App (.NET Framework) for a new
project and place the following components on the main form of the
project:
RichTextBox (named richTextBox1);
ComboBox (named comboBox1);
TextBox (named textBox1);
two Labels (named label1 and label2);
Button (named button1).

The design area with components may look like the following (Fig.4).

10
Fig.4

After the project has been created, we should add the directive

using FTD2XX_NET;

at the beginning of the code (file Form1.cs, by default) and add the
reference to FTD2XX_NET to the References section of our project.
Remind that our project will use FTD2XX_NET.dll library so the files

FTD2XX_NET.dll
FTD2XX_NET.XML

should be put into the working directory of the C# .NET project just
created.

The C# .NET source code (Form1.cs) of the Windows application is


shown in Listing 1.

Listing 1.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;

11
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using FTD2XX_NET;

namespace FT4232H_Example_1
{
public partial class Form1 : Form
{
public byte mask = 0xff; // all pins are outputs (0 = input, 1 =
output)
public byte mode = 0x01; // put a device into async. bit-bang
mode

public uint baudRate = 1000000; // the Baud Rate value


public byte[] bWrite = new byte[1] { 0x0 }; // data to be written to the
channel
public uint bytesWritten = 0; // the number of bytes
written

public FTDI gpout = new FTDI();


public UInt32 ftdiDeviceCount;
public FTDI.FT_STATUS ftStatus;

public FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)


{
ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;

// Determine the number of FTDI devices connected to the machine


ftStatus = gpout.GetNumberOfDevices(ref ftdiDeviceCount);
// Check status

12
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// If no devices available, terminate the application


if (ftdiDeviceCount == 0)
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Allocate storage for device info list


ftdiDeviceList = new
FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

// Populate our device list


ftStatus = gpout.GetDeviceList(ftdiDeviceList);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() + "\r");
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +

13
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r");
comboBox1.Items.Add(ftdiDeviceList[i].Description.ToString());
}
}
}

private void button1_Click(object sender, EventArgs e)


{
bWrite[0] = Convert.ToByte(textBox1.Text);
ftStatus = gpout.Write(bWrite, 1, ref bytesWritten);
}

private void Form1_FormClosing(object sender,


FormClosingEventArgs e)
{
if (gpout.IsOpen) gpout.Close();
}

private void comboBox1_SelectedIndexChanged(object sender,


EventArgs e)
{
if (gpout.IsOpen) gpout.Close();
gpout = new FTDI();
int index = comboBox1.SelectedIndex;
// Open device using its serial number
ftStatus =
gpout.OpenBySerialNumber(ftdiDeviceList[index].SerialNumber); //
device (channel) A = 0,

// B = 1, // C = 2, D = 3

if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

14
ftStatus = gpout.SetBitMode(mask, mode);
// Set up device data parameters

// Set Baud rate


ftStatus = gpout.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
}
}

After the application has started, the configuration of the USB FT4232H
device will be displayed in the richTextBox1 editor.
Then we can select one of four channels (A, B, C or D) that will be used
for control of an interrupt line of the Arduino Zero. To select a suitable
channel, we will use the comboBox1 component labeled Select
Channel.
Also we need to type the decimal value between 0 and 255 in the textBox1
text field. Finally, we can write the value just typed to the selected
channel. The binary code corresponding to the value typed in textBox1
will appear on the output pins of the selected channel.
Lets discuss the above code in detail.

To operate with some channel, the program code use two classes:

FTDI.FT_DEVICE_INFO_NODE
FTDI

FTDI.FT_DEVICE_INFO_NODE (object ftdiDeviceList) is used for


retrieving the information concerning FTDI devices (channels). This
information may be used for further operations with the channels.
The FTDI class (object gpout) allows to get access to a particular device
and perform read/write operations. The state of the recent operation for the
particular channel is reflected by the ftStatus variable of type
FTDI.FT_STATUS.

15
The information concerning FTDI devices of FT4232H chip will be
retrieved when the application is launched (the Form1_Load event
handler). To start operations with some channel, we should first select it
from the list available in the comboBox1 component. Once selected, the
channel will be configured in the comboBox1_SelectedIndexChanged
event handler as described below.

First, we should ensure that the selected channel is closed after possible
previous operations. If OK, we can create a new object gpout. These
operations are executed by the following sequence:

if (gpout.IsOpen) gpout.Close();
gpout = new FTDI();

Then we will open the channel using its serial number That is performed
by the function

ftStatus =
gpout.OpenBySerialNumber(ftdiDeviceList[index].SerialNumber);

The value of the index variable determines the channel to be opened. If,
for example, index = 0, the channel A will be opened; if index = 2, channel
B will be opened, etc. Note that instead of a serial number, we can also use
other parameters to open a channel.
Once a selected channel is open, we can set its parameters. The function

ftStatus = gpout.SetBitMode(mask, mode);

configures mode and I/O direction for the given channel. In this particular
example, the channel will operate in Asynchronous Bit-Bang mode (mode
= 0x01). In this mode, all 8 bits of the channel are read/written in parallel
without clocking. The mask variable (=0xff) configures all 8 bits of the
channel as digital outputs.
We also need to set baud rate for the channel by execution a statement

ftStatus = gpout.SetBaudRate(baudRate);

That is all what is needed to configure a particular channel for

16
asynchronous bit-bang write operation.

The 8-bit value in the range of 0 255 can be typed in the textBox1 field
labeled Byte to write (0-255). To write the value just entered to the
selected output channel, press button1 (Write a Byte). In this case, the
button1_Click event handler is called to perform the write operation. In
this handler, the statement

bWrite[0] = Convert.ToByte(textBox1.Text);

converts the text string entered in the edit field of textBox1 into a byte.
The result is moved into the first element of the bWrite array.
The statement that follows simply writes the byte held in bWrite[0] to the
digital outputs of the channel:

ftStatus = gpout.Write(bWrite, 1, ref bytesWritten);

While testing this example, we should enter either 0 or 1 in the textBox1


edit field so that to toggle the on-board LED of the Arduino Zero.

In this example, we also need to develop an application running on the


Arduino Zero. The source code of such application is shown in Listing 2.

Listing 2.

int busy = 0;

void setup() {
// put your setup code here, to run once:

pinMode(13, OUTPUT);

pinMode(8, INPUT_PULLUP); // --> pin 4 of FT4232H


digitalWrite(13, LOW);

attachInterrupt(digitalPinToInterrupt(8), p8ISR, CHANGE);


}

void loop() {
if (busy)

17
{
digitalWrite(13, !digitalRead(13));
delay(300);
}
}

void p8ISR()
{
if (digitalRead(8) == 0x1)
busy = 1;
else busy = 0;
}

In this code, the on-board LED (pin 13 of Arduino) is toggled each time
when the external interrupt is triggered on pin 8. The interrupt occurs, the
Interrupt Service routine p8ISR() drives the LED ON/OFF.

The window of the running application is shown in Fig.5.

Fig.5

Example 2
In this example, we start/stop timer running on the Arduino Zero through
an external interrupt driven by FT4232H digital output. The circuit
diagram of this project is shown in Fig.6.

18
Fig.6

In this circuit, the pulse train of 10.0 KHz on pin 10 of Arduino will be
enabled/disabled by the signal arriving on pin 8 from FT4232H (pin 4).

The C# .NET application for this project is the same as that described in
the previous section.
The source code of the Arduino Zero application is shown in Listing 3.

Listing 3.

#include <Arduino.h>
#include <Adafruit_ASFcore.h>
#include "Adafruit_ZeroTimer.h"

// creating a timer
Adafruit_ZeroTimer zt4 = Adafruit_ZeroTimer(4);

// timer 4 callback function

19
void T4Callback0(struct tc_module *const module_inst)
{
digitalWrite(10, !digitalRead(10));
}

void setup() {
pinMode(10, OUTPUT);
pinMode(8, INPUT_PULLUP); // --> to pin 4 of FT4232H
attachInterrupt(digitalPinToInterrupt(8), p8ISR, CHANGE);

/********************* Timer #4, 16 bit, one callback with adjustable


period = 10KHz */

zt4.configure(TC_CLOCK_PRESCALER_DIV64, // DIV1
prescaler
TC_COUNTER_SIZE_16BIT, // 16-bit width of
timer/counter
TC_WAVE_GENERATION_MATCH_PWM // match style
);

// set a period that will be GCLK (48 MHz) / prescaler / period / 2


=~10.0KHz, 1 match, channel 0
zt4.setPeriodMatch(37, 1, 0);
// set callback for ch.0 of Timer 4
zt4.setCallback(true, TC_CALLBACK_CC_CHANNEL0, T4Callback0);
// start the timer
zt4.enable(true);
}

void loop() {}

void p8ISR()
{
if (digitalRead(8) == 0x1)
zt4.enable(true);
else zt4.enable(false);
}

When designing this application, you should install two libraries from
Adafruit, Adafruit_ASFcore and Adafruit_ZeroTimer. In this code,

20
Timer 4 (zt4) of the ATSAMD21G18 microcontroller produces the pulse
train of 10 KHz on pin 10 of the Arduino Zero. Timer 4 is
enabled/disabled by the C# .NET application through the interrupt
configured on pin 8 of Arduino.
When the signal on pin 8 of Arduino changes from either LOW(log.0) to
HIGH or HIGH to LOW, the interrupt is triggered. The logical level on pin
8 is tested by the Interrupt Service Routine p8ISR() that then
enables/disables Timer 4, depending on the result of test.

Example 3
This example is the advanced version of the previous one. Here the C#
.NET application will enable/disable Timer 3 and Timer 4 of the
ATSAMD21G18 MCU thus affecting two LEDs. Timer 3 drives the on-
board LED (pin 13 of Arduino), while Timer 4 drives the LED network
connected to pin 12.
The circuit diagram of the project is shown in Fig.7.

Fig.7

Timer 3 and Timer 4 are controlled by C# .NET application through two

21
external interrupts (pins 8 and 9 of the Arduino Zero). Channel A of
FT4232H controls Timer 3 by triggering the interrupt on pin 8 of Arduino,
while channel B of FT4232H controls Timer 4 by triggering the interrupt
on pin 9. Both channels are put in the Asynchronous Bit-Bang mode.

The main form of the C# .NET application looks like the following
(Fig.8).

Fig.8

In this project, we use the following components:


RichTextBox (named richTextBox1);
two CheckBox components (named channelA and channelB);
two TextBox components (named textBox1 and textBox2);
two Labels (named label1 and label2);
Button (named button1);
two Label components (named label1 and label2);
two Panel components (named panel1 and panel2).

This application allows to write a value in the range 0-255 to the selected
channel (A and/or B) of FT4232H. The values to be written to outputs
should be entered in textBox1 and textBox2. Clicking the button Write
to Ports causes the values entered in textBox1 and textBox2 to be
written to the corresponding channel.
The C# .NET source code (Form1.cs) of the Windows application is
shown in Listing 4.

22
Listing 4.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using FTD2XX_NET;

namespace FT4232H_Example_2
{
public partial class Form1 : Form
{
public byte mask = 0xff; // pins are set as outputs (0 = input, 1
= output)
public byte mode = 0x01; // put a device into async. bit-bang
mode

public uint baudRate = 1000000; // the Baud Rate value


public byte[] bWriteA = new byte[1] { 0x0 }; // data to be written
into channel A
public byte[] bWriteB = new byte[1] { 0x0 }; // data to be written
into channel B

public uint bytesWrittenA = 0; // the number of bytes written into


channel A
public uint bytesWrittenB = 0; // the number of bytes written into
channel B

public FTDI gpout = new FTDI();


public FTDI gpoutA = new FTDI();
public FTDI gpoutB = new FTDI();

public UInt32 ftdiDeviceCount;


public FTDI.FT_STATUS ftStatus;

23
public FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList;

public Form1()
{
InitializeComponent();
}

private void channelA_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked || channelB.Checked)) button1.Enabled =
false;
else button1.Enabled = true;

if (gpoutA.IsOpen) gpoutA.Close();

gpoutA = new FTDI();


// Open channel A using its serial number
ftStatus =
gpoutA.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);

if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutA.SetBitMode(mask, mode);

// Setting up device data parameters


// Set Baud rate

ftStatus = gpoutA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);

24
this.Close();
}
ftStatus = gpoutA.Write(bWriteA, 1, ref bytesWrittenA); //
initially, we write 0 into channel A
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to write to port A (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

private void channelB_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked || channelB.Checked)) button1.Enabled =
false;
else button1.Enabled = true;
if (gpoutB.IsOpen) gpoutB.Close();

gpoutB = new FTDI();


// Open channel B using its serial number
ftStatus =
gpoutB.OpenBySerialNumber(ftdiDeviceList[1].SerialNumber);

if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutB.SetBitMode(mask, mode);

// Setting up device data parameters


// Set Baud rate
ftStatus = gpoutB.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +

25
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutB.Write(bWriteB, 1, ref bytesWrittenB); // initially
we write 0 into channel B
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to write to the port B (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

private void button1_Click(object sender, EventArgs e)


{
if (channelA.Checked)
{
bWriteA[0] = Convert.ToByte(textBox1.Text);
ftStatus = gpoutA.Write(bWriteA, 1, ref bytesWrittenA);
}
if (channelB.Checked)
{
bWriteB[0] = Convert.ToByte(textBox2.Text);
ftStatus = gpoutB.Write(bWriteB, 1, ref bytesWrittenB);
}
}

private void Form1_Load(object sender, EventArgs e)


{
channelA.Checked = false;
channelB.Checked = false;
button1.Enabled = false;

textBox1.Text = "0";
textBox2.Text = "0";

ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;

26
// Determine the number of FTDI devices connected to the machine
ftStatus = gpout.GetNumberOfDevices(ref ftdiDeviceCount);
// Check status
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// If no devices available, exit


if (ftdiDeviceCount == 0)
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Allocate storage for device info list


ftdiDeviceList = new
FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

// Populate our device list


ftStatus = gpout.GetDeviceList(ftdiDeviceList);

if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() +
"\r");
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +

27
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r");
}
}
gpout.Close();
}

private void Form1_FormClosing(object sender,


FormClosingEventArgs e)
{
gpoutA.Close();
gpoutB.Close();
}
}
}

The Source code of the Arduino application is shown in Listing 5.

Listing 5.

#include <Arduino.h>
#include <Adafruit_ASFcore.h>
#include "Adafruit_ZeroTimer.h"

int cnt3 = 0;
int cnt4 = 0;

// creating timer objects


Adafruit_ZeroTimer zt3 = Adafruit_ZeroTimer(3);
Adafruit_ZeroTimer zt4 = Adafruit_ZeroTimer(4);

// Timer 3 callback

28
void T3Callback0(struct tc_module *const module_inst)
{
cnt3++;
if (cnt3 > 100)
{
cnt3 = 0;
digitalWrite(12, !digitalRead(12));
}
}

// Timer 4 callback

void T4Callback0(struct tc_module *const module_inst)


{
cnt4++;
if (cnt4 > 100)
{
cnt4 = 0;
digitalWrite(13, !digitalRead(13));
}
}

void setup() {
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);

pinMode(8, INPUT_PULLUP); // --> pin 4 of FT4232H (ch.A bit 0)


pinMode(9, INPUT_PULLUP); // --> pin 14 of FT4232H (ch.B bit
0)
attachInterrupt(digitalPinToInterrupt(8), p8ISR, CHANGE);
attachInterrupt(digitalPinToInterrupt(9), p9ISR, CHANGE);

/********************* Timer #3, 16 bit, one callback with adjustable


period = 380 Hz */

zt3.configure(TC_CLOCK_PRESCALER_DIV256, // prescaler
TC_COUNTER_SIZE_16BIT, // 16-bit
width of timer/counter
TC_WAVE_GENERATION_MATCH_PWM // match

29
style
);

// Set Timer 3 frequency to GCLK (48 MHz) / prescaler / period / 2 =~ 380


Hz, 1 match, channel 0

zt3.setPeriodMatch(250, 1, 0);

// set callback for ch.0 of Timer 3

zt3.setCallback(true, TC_CALLBACK_CC_CHANNEL0, T3Callback0);

zt3.enable(true);

/********************* Timer #4, 16 bit, one callback with adjustable


period = 380 Hz */

zt4.configure(TC_CLOCK_PRESCALER_DIV256, // prescaler
TC_COUNTER_SIZE_16BIT, // 16-bit
width of timer/counter
TC_WAVE_GENERATION_MATCH_PWM // match
style
);

// Set Timer 4 frequency to GCLK (48 MHz) / prescaler / period / 2 =~ 380


Hz, 1 match, channel 0

zt4.setPeriodMatch(250, 1, 0);

// set callback for ch.0 of Timer 4

zt4.setCallback(true, TC_CALLBACK_CC_CHANNEL0, T4Callback0);


zt4.enable(true);
}

void loop() {}

void p8ISR()
{
if (digitalRead(8) == 0x1)

30
zt3.enable(true);
else zt3.enable(false);
}

void p9ISR()
{
if (digitalRead(9) == 0x1)
zt4.enable(true);
else zt4.enable(false);
}

In this code, the timer callback functions T3Callback0() and


T3Callback0() provide additional delay (variables cnt3 and cnt4,
respectively) so that both LEDs can blink at slow speed.
The p8ISR() and p9ISR() interrupt handlers enable/disable Timer 3 and
Timer 4, respectively.

The window of the running C# .NET application is shown in Fig.9.

Fig.9

You can easily improve the above C# .NET and Arduino code to control
more peripherals.

Example 4
31
This example illustrates how to configure the duty cycle of a PWM signal
of the Arduino Zero by a C# .NET application. The circuit diagram of the
project is shown in Fig.10.

Fig.10

In this circuit, a data byte from channel A of FT4232H determines the duty
cycle of the PWM signal generated on pin 11 of the Arduino Zero. The C#
.NET program code writes the data byte into channel A connected to pins
0-7 on the Arduino board, then triggers an interrupt on pin 8 of Arduino by
either rising or falling edge. The Interrupt Service Routine associated with
this interrupt enables reading the data from channel A by the Arduino
program code.
Below is a table that details the connections between the Arduino Zero and
FT4232H board for this project.

Arduino FT4232H pin


Zero pin
0 4 (channel A bit 0)
1 3 (channel A bit 1)
2 6 (channel A bit 2)

32
3 5 (channel A bit 3)
4 8 (channel A bit 4)
5 7 (channel A bit 5)
6 10 (channel A bit 6)
7 9 (channel A bit 7)
8 14 (channel B bit 0) -
strobe

The source code of the C# .NET application is the same as that described
in the previous section.
The source code of the Arduino application is shown in Listing 6.

Listing 6.

int i1;
volatile int done = 0;
int Din;
int res[8];

void setup() {
// put your setup code here, to run once:
for (i1 = 0; i1 < 9; i1++)
pinMode(i1, INPUT_PULLUP); // an ext. interrupt is assigned pin 8

pinMode(11, OUTPUT); // PWM output


attachInterrupt(digitalPinToInterrupt(8), p8ISR, CHANGE);
analogWriteResolution(8);
analogWrite(11, 128); // configure a PWM output with a duty = 50%
}

void loop() {
// put your main code here, to run repeatedly:
if (done == 1)
{
Din = 0;
for (i1 = 0; i1 < 8; i1++)
res[i1] = digitalRead(i1);
for (i1 = 0; i1 < 8; i1++)

33
Din += (res[i1] & 0x1) << i1;
Din = Din & 0xFF;
analogWrite(11, Din); // configure a PWM output with a duty = Din
done = 0;
}
}

void p8ISR(){
done = 1;
}

In this code, the Interrupt Service Routine p8ISR() when called simply
sets the variable done. This allows the data bits on pins 0-7 of Arduino to
be read and saved in the variable Din. The value of Din is then used for
configuring a duty cycle of the PWM signal on pin 11. The following
sequence performs these operations:

Din = 0;
for (i1 = 0; i1 < 8; i1++)
res[i1] = digitalRead(i1);
for (i1 = 0; i1 < 8; i1++)
Din += (res[i1] & 0x1) << i1;
Din = Din & 0xFF;
analogWrite(11, Din);

To configure the duty cycle of PWM from C# .NET application, enter the
desired value in the text field for channel A. Then, in the text field
corresponding to channel B type the corresponding value and click the
Write button. Note that you should change the value for channel B from
0 to 1, and vice versa each time when you write a data byte to
Arduino.

Example 5
This project allows to set the frequency of the tone generator created by
the Arduino code. The circuit diagram is shown in Fig.11.

34
Fig.11

In this circuit, a 10-bit value from channels A and B of FT4232H board


determine the frequency of the tone generator providing the output signal
on pin 12 of the Arduino Zero. The C# .NET program code writes the 10-
bit value into channels A and B connected to pins 0-9 on the Arduino
board, then triggers an interrupt on pin 10 of Arduino by a rising edge thus
allowing to read the data by the Arduino program code.

Below is a table that details the connections between the Arduino Zero and
FT4232H board for this project.

Arduino FT4232H pin


Zero pin
0 4 (channel A bit 0)
1 3 (channel A bit 1)
2 6 (channel A bit 2)
3 5 (channel A bit 3)
4 8 (channel A bit 4)
5 7 (channel A bit 5)
6 10 (channel A bit 6)

35
7 9 (channel A bit 7)
8 14 (channel B bit 0)
9 13 (channel B bit 1)
10 21 (channel C bit 1)

The main form of the C# .NET application together with components is


shown in Fig.12.

Fig.12

The source code of the C# .NET (file Form1.cs) application is shown in


Listing 7.

Listing 7.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

36
using System.Threading;
using FTD2XX_NET;

namespace FT4232H_Example_3
{
public partial class Form1 : Form
{
public byte mask = 0xff; // configuring pins as outputs(0 =
input, 1 = output)
public byte mode = 0x01; // put a device into async. bit-bang
mode

public uint baudRate = 1000000; // the Baud Rate value


public byte[] bWriteA = new byte[1] { 0x0 }; // data to be written
into the channel A (low byte)
public byte[] bWriteB = new byte[1] { 0x0 }; // data to be written
into the channel B (high byte)
public byte[] bWriteC = new byte[1] { 0x0 }; // data to be written
into the channel C --> the strobe
// to pin 10 of Arduino

public uint bytesWrittenA = 0; // the number of bytes written into


channel A
public uint bytesWrittenB = 0; // the number of bytes written into
channel B
public uint bytesWrittenC = 0; // the number of bytes written into
channel C

public UInt16 Din; // the value for configuring a frequency

public FTDI gpout = new FTDI();


public FTDI gpoutA = new FTDI();
public FTDI gpoutB = new FTDI();
public FTDI gpoutC = new FTDI();

public UInt32 ftdiDeviceCount;


public FTDI.FT_STATUS ftStatus;

public FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList;

37
public Form1()
{
InitializeComponent();
}

private void channelA_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked)) button1.Enabled = false;
else button1.Enabled = true;
if (gpoutA.IsOpen) gpoutA.Close();
gpoutA = new FTDI();

// Open device using its serial number


ftStatus =
gpoutA.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber); // device
A = 0, B = 1

// C = 2, D = 3

if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutA.SetBitMode(mask, mode);

// Set up device data parameters


// Set Baud rate

ftStatus = gpoutA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();

38
}
}

private void channelB_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked)) button1.Enabled = false;
else button1.Enabled = true;
if (gpoutB.IsOpen) gpoutB.Close();

gpoutB = new FTDI();

// Open device using its serial number


ftStatus =
gpoutB.OpenBySerialNumber(ftdiDeviceList[1].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutB.SetBitMode(mask, mode);

// Set up device data parameters


// Set Baud rate

ftStatus = gpoutB.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

private void button1_Click(object sender, EventArgs e)


{
if (channelA.Checked && channelB.Checked &&

39
channelC.Checked)
{
byte[] bytes = new byte[2];
Din = Convert.ToUInt16(textBox1.Text);
if (Din >= 10 && Din <= 1000)
{
bytes = BitConverter.GetBytes(Din); // bytes[0] contains a low
byte of Din,
// bytes[1] contains a high
byte
bWriteA[0] = bytes[0]; // a low byte of UInt16 value goes to
channel A
bWriteB[0] = bytes[1]; // a high byte of UInt16 value goes to
channel B

bWriteA[0] = bytes[0];
bWriteB[0] = bytes[1];

ftStatus = gpoutA.Write(bWriteA, 1, ref bytesWrittenA);


ftStatus = gpoutB.Write(bWriteB, 1, ref bytesWrittenB);

// writing a strobe 0-->1 to pin 10 of Arduino

bWriteC[0] = 0x0; // bit 0 of channel C is brought LOW


ftStatus = gpoutC.Write(bWriteC, 1, ref bytesWrittenC);
Thread.Sleep(1);
bWriteC[0] = 0x2; // bit 1 of channel C is brought HIGH
ftStatus = gpoutC.Write(bWriteC, 1, ref bytesWrittenC);
}
else MessageBox.Show("The value is out of range.", "Error",
MessageBoxButtons.OK);
}
}

private void Form1_Load(object sender, EventArgs e)


{
channelA.Checked = false;
channelB.Checked = false;
channelC.Checked = false;
button1.Enabled = false;

40
textBox1.Text = "100"; // an initial value

ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;

// Determine the number of FTDI devices connected to the machine


ftStatus = gpout.GetNumberOfDevices(ref ftdiDeviceCount);
// Check status
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// If no devices available, exit


if (ftdiDeviceCount == 0)
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Allocate storage for device info list


ftdiDeviceList = new
FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

// Populate our device list


ftStatus = gpout.GetDeviceList(ftdiDeviceList);

if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() +

41
"\r");
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r");
}
}
gpout.Close();
}

private void Form1_FormClosing(object sender,


FormClosingEventArgs e)
{
gpoutA.Close();
gpoutB.Close();
gpoutC.Close();
}

private void channelC_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked)) button1.Enabled = false;
else button1.Enabled = true;
if (gpoutC.IsOpen) gpoutC.Close();
gpoutC = new FTDI();

// Open device using its serial number


ftStatus =
gpoutC.OpenBySerialNumber(ftdiDeviceList[2].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +

42
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutC.SetBitMode(mask, mode);

// Set up device data parameters


// Set Baud rate

ftStatus = gpoutC.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
}
}

The source code of the Arduino Zero application is shown in Listing 8.

Listing 8.

int i1;
volatile int done = 0;
int Din, K = 10; // freq.res = 10 Hz
int res[10];

void setup() {
// put your setup code here, to run once:
for (i1 = 0; i1 < 11; i1++)
pinMode(i1, INPUT_PULLUP); // 10 data bits + an ext. interrupt pin 10
are inputs

attachInterrupt(digitalPinToInterrupt(10), p10ISR, RISING);


tone (12, 1400); // initial frequency = 1400 Hz
}

43
void loop() {
// put your main code here, to run repeatedly:
if (done == 1)
{
Din = 0;
for (i1 = 0; i1 < 10; i1++)
res[i1] = digitalRead(i1);
for (i1 = 0; i1 < 10; i1++)
Din += (res[i1] & 0x1) << i1;
Din = Din & 0x3FF;
tone(12, K * Din); // configure an oscillator output with a freq = K x Din
done = 0;
}
}

void p10ISR(){
done = 1;
}

In this code, the initial frequency (1400 Hz) of the tone generator is
configured in the setup() function:

tone (12, 1400);

To configure the output frequency on the fly we will use the following
statement:

tone(12, K * Din);

where K = 10. In this case, the output frequency will be the value held in
the Din variable multiplied by K.

The window of the running C# .NET application is shown in Fig.13.

44
Fig.13

In this window, the output frequency will be set to 380 x 10 = 3800 Hz.

Example 6
The Example 5 can be easily modified for configuring a digital-to-analog
converter (DAC) of the Arduino Zero. In this project the 10-bit binary
code from FT4232H will be used for setting the resolution of the DAC.
The circuit diagram of the hardware is shown in Fig.14.

45
Fig.14

In this circuit, the 10-bit binary code from channel A of FT4232H is fed to
pins 0-9 of the Arduino Zero. This code is then read by the Arduino
program code after triggering the interrupt on pin 10 of Arduino by the
rising edge from pin 21 of the FT4232H board.
The main form of the C# .NET windows application is shown in Fig.15.

46
Fig.15

The C# .NET source code (file Form1.cs) that controls Arduino is shown
below (Listing 9).

Listing 9.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using FTD2XX_NET;

namespace FT4232H_Example_4
{
public partial class Form1 : Form
{
public byte mask = 0xff; // configuring pins as outputs (0 =
input, 1 = output)
public byte mode = 0x01; // put a device into async. bit-bang
mode

public uint baudRate = 1000000; // the Baud Rate value


public byte[] bWriteA = new byte[1] { 0x0 }; // data to be written
into channel A (low byte)
public byte[] bWriteB = new byte[1] { 0x0 }; // data to be written
into channel B (high byte)
public byte[] bWriteC = new byte[1] { 0x0 }; // channel C provides
the strobe to pin 10 of Arduino

public uint bytesWrittenA = 0; // the number of bytes written into


channel A
public uint bytesWrittenB = 0; // the number of bytes written into
channel B

47
public uint bytesWrittenC = 0; // the number of bytes written into
channel C

public UInt16 Din; // the value for configuring a frequency

public FTDI gpout = new FTDI();


public FTDI gpoutA = new FTDI();
public FTDI gpoutB = new FTDI();
public FTDI gpoutC = new FTDI();

public UInt32 ftdiDeviceCount;


public FTDI.FT_STATUS ftStatus;

public FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList;

public Form1()
{
InitializeComponent();
}

private void channelA_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked)) button1.Enabled = false;
else button1.Enabled = true;
if (gpoutA.IsOpen) gpoutA.Close();
gpoutA = new FTDI();

// Open device using its serial number


ftStatus =
gpoutA.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);

if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutA.SetBitMode(mask, mode);

48
// Set up device data parameters

// Set Baud rate


ftStatus = gpoutA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

private void channelB_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked)) button1.Enabled = false;
else button1.Enabled = true;
if (gpoutB.IsOpen) gpoutB.Close();
gpoutB = new FTDI();

// Open device using its serial number


ftStatus =
gpoutB.OpenBySerialNumber(ftdiDeviceList[1].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutB.SetBitMode(mask, mode);
// Set up device data parameters
// Set Baud rate

ftStatus = gpoutB.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +

49
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

private void button1_Click(object sender, EventArgs e)


{
if (channelA.Checked && channelB.Checked &&
channelC.Checked)
{
byte[] bytes = new byte[2]; // array of bytes to store 10-bit value
double LSB = 3.33 / 1024; // LSB for 10-bit DAC
double vDAC = Convert.ToDouble(textBox1.Text); // contains the
desired DAC voltage
if (vDAC > 0 && vDAC <= 3.2) // DAC output must between 0
and 3.2V
{
Din = (UInt16)(vDAC / LSB);
bytes = BitConverter.GetBytes(Din); // bytes[0] contains a low
byte of Din,
// bytes[1] contains a high
byte of Din
bWriteA[0] = bytes[0]; // a low byte of Din goes to channel A
bWriteB[0] = bytes[1]; // a high byte of Din goes to channel B

// Writing the 10-bit DAC code into channels A-B

ftStatus = gpoutA.Write(bWriteA, 1, ref bytesWrittenA); // check


ftStatus if needed
ftStatus = gpoutB.Write(bWriteB, 1, ref bytesWrittenB);

// writing a strobe 0-->1 to pin 10 of Arduino

bWriteC[0] = 0x0; // bit 0 of channel C will be brought LOW


ftStatus = gpoutC.Write(bWriteC, 1, ref bytesWrittenC);
Thread.Sleep(1);
bWriteC[0] = 0x2; // bit 1 of channel C will be brought HIGH
ftStatus = gpoutC.Write(bWriteC, 1, ref bytesWrittenC);
}

50
else MessageBox.Show("The value is out of range.", "Error",
MessageBoxButtons.OK);
}
}

private void Form1_Load(object sender, EventArgs e)


{
channelA.Checked = false;
channelB.Checked = false;
channelC.Checked = false;
button1.Enabled = false;

textBox1.Text = "0.1"; // initial value for the DAC output, Volts


ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;

// Determine the number of FTDI devices connected to the machine


ftStatus = gpout.GetNumberOfDevices(ref ftdiDeviceCount);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// If no devices available, exit


if (ftdiDeviceCount == 0)
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Allocate storage for device info list


ftdiDeviceList = new

51
FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

// Populate our device list


ftStatus = gpout.GetDeviceList(ftdiDeviceList);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() + "\r");
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r");
}
}
gpout.Close();
}

private void Form1_FormClosing(object sender,


FormClosingEventArgs e)
{
gpoutA.Close();
gpoutB.Close();
gpoutC.Close();
}

private void channelC_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked)) button1.Enabled = false;
else button1.Enabled = true;
if (gpoutC.IsOpen) gpoutC.Close();

52
gpoutC = new FTDI();

// Open device using its serial number


ftStatus =
gpoutC.OpenBySerialNumber(ftdiDeviceList[2].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutC.SetBitMode(mask, mode);

// Set up device data parameters


// Set Baud rate
ftStatus = gpoutC.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
}
}

The source code of the Arduino Zero application is shown in Listing 10.

Listing 10.

int i1;
volatile int done = 0;
int Din; // a value for configuring DAC
int res[10];

void setup() {

53
// put your setup code here, to run once:
for (i1 = 0; i1 < 11; i1++)
pinMode(i1, INPUT_PULLUP); // 10 data bits + an ext. interrupt pin 10
are inputs

attachInterrupt(digitalPinToInterrupt(10), p10ISR, RISING);


analogWriteResolution(10); // the resolution of DAC is set to 10 bit
analogWrite(A0, 512); // initial DAC output = 1.65V at the reference
of 3.3V
}

void loop() {
if (done == 1)
{
Din = 0;
for (i1 = 0; i1 < 10; i1++)
res[i1] = digitalRead(i1);
for (i1 = 0; i1 < 10; i1++)
Din += (res[i1] & 0x1) << i1;
Din = Din & 0x3FF;
analogWrite(A0, Din); // configure the DAC output voltage
done = 0;
}
}

void p10ISR(){
done = 1;
}

The window of the running C# .NET application is shown in Fig.16.

54
Fig.16

The above window illustrates configuring the DAC output voltage to be


3.05V.

Example 7
This example illustrates how to read external signals on FT4232H digital
inputs. The test signals to FT4232H are fed from pins 0-9 of the Arduino
Zero. The circuit diagram of the project is shown in Fig.17.

55
Fig.17

In this circuit, the 10-bit data is read from pins 0-9 of Arduino into
channels A-B of FT4232H after a rising edge from channel C arrives at pin
10 of Arduino.
The main form of the C# .NET application is shown in Fig.18.

56
Fig.18

The source code for the C# .NET windows application is shown in Listing
11.

Listing 11.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using FTD2XX_NET;

namespace FT4232H_Example_5
{
public partial class Form1 : Form
{
byte[] bytes = new byte[2]; // array of bytes to store 10-bit value
public byte maskR = 0x00; // mask for configuring pins as inputs

57
public byte maskW = 0xff; // mask for configuring pins as outputs
public byte mode = 0x01; // put a device into async. bit-bang mode

public uint baudRate = 1000000; // the Baud Rate value


public byte[] bReadA = new byte[1] { 0x0 }; // data to be read into
channel A (low byte)
public byte[] bReadB = new byte[1] { 0x0 }; // data to be read into
channel B (high byte)
public byte[] bWriteC = new byte[1] { 0x0 }; // clocking is provided
via bit 1 of channel C
// the strobe goes to pin 10
of Arduino

public uint bytesReadA = 0; // the number of bytes read via channel A


public uint bytesReadB = 0; // the number of bytes read via channel B
public uint bytesWrittenC = 0; // the number of bytes written into
channel C

public UInt16 Din; // the variable to hold the 10-bit data obtained

public FTDI gpout = new FTDI();


public FTDI gpoutA = new FTDI();
public FTDI gpoutB = new FTDI();
public FTDI gpoutC = new FTDI();

public uint FT_PURGE_RX = 1;


public UInt32 ftdiDeviceCount;
public FTDI.FT_STATUS ftStatus;

public FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList;

public Form1()
{
InitializeComponent();
}

private void channelA_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked)) button1.Enabled = false;

58
else button1.Enabled = true;
if (gpoutA.IsOpen) gpoutA.Close();
gpoutA = new FTDI();

// Open device using its serial number


ftStatus =
gpoutA.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutA.SetBitMode(maskR, mode); // configuring
channel A for input

// Set up device data parameters


// Set Baud rate

ftStatus = gpoutA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

private void channelB_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked)) button1.Enabled = false;
else button1.Enabled = true;
if (gpoutB.IsOpen) gpoutB.Close();
gpoutB = new FTDI();

// Open device using its serial number


ftStatus =

59
gpoutB.OpenBySerialNumber(ftdiDeviceList[1].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutB.SetBitMode(maskR, mode);

// Set up device data parameters


// Set Baud rate

ftStatus = gpoutB.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

private void button1_Click(object sender, EventArgs e)


{
if (channelA.Checked && channelB.Checked &&
channelC.Checked)
{
// writing a strobe 0-->1 to pin 10 of Arduino

bWriteC[0] = 0x0; // bit 1 of channel C is brought LOW


ftStatus = gpoutC.Write(bWriteC, 1, ref bytesWrittenC);
Thread.Sleep(1);
bWriteC[0] = 0x2; // bit 1 of channel C is brought HIGH
ftStatus = gpoutC.Write(bWriteC, 1, ref bytesWrittenC);

Thread.Sleep(10);

ftStatus = gpoutA.Read(bReadA, 1, ref bytesReadA); // a low byte


goes to channel A

60
ftStatus = gpoutB.Read(bReadB, 1, ref bytesReadB); // a high byte
goes to channel B
bytes[0] = bReadA[0];
bytes[1] = bReadB[0];
ftStatus = gpoutA.Purge(FT_PURGE_RX); // purge the receive
buffer of channel A
ftStatus = gpoutB.Purge(FT_PURGE_RX); // purge the receive
buffer of channel B

Thread.Sleep(10);

Din = BitConverter.ToUInt16(bytes, 0);


Din = (UInt16)(Din & 0x3FF);
textBox1.Text = Convert.ToString(Din);
}
}

private void Form1_Load(object sender, EventArgs e)


{
channelA.Checked = false;
channelB.Checked = false;
channelC.Checked = false;
button1.Enabled = false;

textBox1.Text = "0";

ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;

// Determine the number of FTDI devices connected to the machine


ftStatus = gpout.GetNumberOfDevices(ref ftdiDeviceCount);
// Check status
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);

61
this.Close();
}

// If no devices available, return


if (ftdiDeviceCount == 0)
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Allocate storage for device info list


ftdiDeviceList = new
FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

// Populate our device list


ftStatus = gpout.GetDeviceList(ftdiDeviceList);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() + "\r");
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r");
}
}
gpout.Close();
}

62
private void Form1_FormClosing(object sender,
FormClosingEventArgs e)
{
gpoutA.Close();
gpoutB.Close();
gpoutC.Close();
}

private void channelC_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked)) button1.Enabled = false;
else button1.Enabled = true;
if (gpoutC.IsOpen) gpoutC.Close();
gpoutC = new FTDI();

// Open device using its serial number


ftStatus =
gpoutC.OpenBySerialNumber(ftdiDeviceList[2].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutC.SetBitMode(maskW, mode);

// Set up device data parameters


// Set Baud rate
ftStatus = gpoutC.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
}

63
}

The source code of the Arduino application is shown in Listing 12.

Listing 12.

int i1;
int tmp, Din;
int done = 0;

void setup() {
for (i1 = 0; i1 < 10; i1++)
pinMode(i1, OUTPUT); // 10 data bits are outputs

pinMode(10, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(10), p10ISR, RISING);
Din = 0;
}

void loop() {
if (done == 1)
{
Din += 1;
if (Din > 32) Din = 0;
setOutputs(Din);
done = 0;
}
}

void setOutputs(int Din)


{
for (i1 = 0; i1 < 10; i1++)
digitalWrite(i1, LOW); // Clear all 10 outputs
tmp = 0;
for (i1 = 0; i1 < 10; i1++)
{
tmp = (Din >> i1) & 0x1;
digitalWrite(i1, tmp);
}

64
}

void p10ISR(){
done = 1;
}

In this code, the 10-bit binary data is output on pins 0-9 of the Arduino
Zero by the setOutputs() function which is called each time when the
interrupt on pin 10 of Arduino is triggered. The function takes a single
parameter Din that changes from 0 to 32 each time the interrupt occurs.

The window of the running C# .NET application is shown in Fig.19.

Fig.19

Clicking the button Read Ports causes the value in the text editor to be
incremented by 1 until the input code exceeds 32. Then the input code
starts incrementing from 0.

Example 8
This project shows how to measure analog voltage on pin A2 of the
Arduino Zero and transfer the data obtained to FT4232H. The circuit

65
diagram of the project is shown in Fig.20.

Fig.20

In this circuit, the test voltage to channel A2 of the Arduino Zero analog-
to-digital converter (ADC) is fed from the wiper of potentiometer R1.
ADC is configured to operate with resolution of 10 bit, therefore after
conversion is complete we obtain the 10-bit binary code corresponding to
the analog voltage on pin A2.
This code then appears on digital output pins 0-9 of Arduino followed by
strobe on pin 10 of Arduino. Bringing pin 10 HIGH allows the 10-bit data
to be read via FT4232H by C# .NET application.

The main form of C# .NET application is shown in Fig.21.

66
Fig.21

The C# .NET source code of the Windows application is shown in Listing


13.

Listing 13.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using FTD2XX_NET;

namespace FT4232H_Example_6
{
public partial class Form1 : Form
{
byte[] bytes = new byte[2]; // byte array to store 10-bit value

67
public byte maskR = 0x00; // all pins are set as inputs (0 = input, 1 =
output)
public byte mode = 0x01; // put a device into async. bit-bang mode

public uint baudRate = 1000000; // the Baud Rate value


public byte[] bReadA = new byte[1] { 0x0 }; // data to be read into
channel A (low byte)
public byte[] bReadB = new byte[1] { 0x0 }; // data to be read into
channel B (high byte)
public byte[] bReadC = new byte[1] { 0x0 }; // checking bit 1 of
channel C
// when bit 1 =1, data is
ready to read

public uint bytesReadA = 0; // the number of bytes read into channel A


public uint bytesReadB = 0; // the number of bytes read into channel B
public uint bytesReadC = 0; // the number of bytes read into channel C

public FTDI gpout = new FTDI();


public FTDI gpoutA = new FTDI();
public FTDI gpoutB = new FTDI();
public FTDI gpoutC = new FTDI();

public uint FT_PURGE_RX = 1;

public UInt32 ftdiDeviceCount;


public FTDI.FT_STATUS ftStatus;

public FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList;

public Thread thread1; // we use an additional thread to read data


public bool thEnabled = false; // initially, thread1 is disabled

public UInt16 Din; // the binary code of A/D conversion


public double LSB = 3.33 / 1024; // LSB for 10-bit ADC
public double Vin; // voltage level on pin A2 of the
Arduino Zero

public Form1()
{

68
InitializeComponent();
}

private void channelA_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked))
{
startADC.Enabled = false;
stopADC.Enabled = false;
}
else
{
startADC.Enabled = true;
stopADC.Enabled = false;
}

if (gpoutA.IsOpen) gpoutA.Close();
gpoutA = new FTDI();

// Open device using its serial number


ftStatus =
gpoutA.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutA.SetBitMode(maskR, mode); // configuring port A
for input

// Set up device data parameters


// Set Baud rate

ftStatus = gpoutA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +

69
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

private void channelB_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked))
{
startADC.Enabled = false;
stopADC.Enabled = false;
}
else
{
startADC.Enabled = true;
stopADC.Enabled = false;
}
if (gpoutB.IsOpen) gpoutB.Close();
gpoutB = new FTDI();

// Open device using its serial number


ftStatus =
gpoutB.OpenBySerialNumber(ftdiDeviceList[1].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutB.SetBitMode(maskR, mode);

// Set up device data parameters


// Set Baud rate

ftStatus = gpoutB.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{

70
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

private void startADC_Click(object sender, EventArgs e)


{
if (channelA.Checked && channelB.Checked &&
channelC.Checked)
{
thEnabled = true;
startADC.Enabled = false;
stopADC.Enabled = true;
}
}

private void Form1_Load(object sender, EventArgs e)


{
channelA.Checked = false;
channelB.Checked = false;
channelC.Checked = false;
startADC.Enabled = false;
stopADC.Enabled = false;

ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;

// Determine the number of FTDI devices connected to the machine


ftStatus = gpout.GetNumberOfDevices(ref ftdiDeviceCount);
// Check status
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);

71
this.Close();
}

// If no devices available, exit


if (ftdiDeviceCount == 0)
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Allocate storage for device info list


ftdiDeviceList = new
FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

// Populate our device list


ftStatus = gpout.GetDeviceList(ftdiDeviceList);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() + "\r");
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r");
}
}
gpout.Close();
thread1 = new Thread(threadProc);
thread1.Start();

72
}

private void Form1_FormClosing(object sender,


FormClosingEventArgs e)
{
gpoutA.Close();
gpoutB.Close();
gpoutC.Close();
thread1.Abort();
Thread.Sleep(10);
}

private void channelC_CheckedChanged(object sender, EventArgs e)


{
if (!(channelA.Checked && channelB.Checked &&
channelC.Checked))
{
startADC.Enabled = false;
stopADC.Enabled = false;
}
else
{
startADC.Enabled = true;
stopADC.Enabled = false;
}
if (gpoutC.IsOpen) gpoutC.Close();
gpoutC = new FTDI();

// Open device using its serial number


ftStatus =
gpoutC.OpenBySerialNumber(ftdiDeviceList[2].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutC.SetBitMode(maskR, mode);

73
// Set up device data parameters
// Set Baud rate

ftStatus = gpoutC.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}

public void threadProc()


{
while (true)
{
if (thEnabled && (channelA.Checked && channelB.Checked &&
channelC.Checked))
{
// check bit 2 of port C; if =1, data is ready to pick up
do
{
ftStatus = gpoutC.Read(bReadC, 1, ref bytesReadC);
} while (bReadC[0] == 0x2);
do
{
ftStatus = gpoutC.Read(bReadC, 1, ref bytesReadC);
} while (bReadC[0] == 0x0);

ftStatus = gpoutA.Read(bReadA, 1, ref bytesReadA); // a low byte


goes to channel A
ftStatus = gpoutB.Read(bReadB, 1, ref bytesReadB); // a high byte
goes to channel B
bytes[0] = bReadA[0];
bytes[1] = bReadB[0];
ftStatus = gpoutA.Purge(FT_PURGE_RX); // clearing receive
buffers
ftStatus = gpoutB.Purge(FT_PURGE_RX);

74
// converting data to string

Din = BitConverter.ToUInt16(bytes, 0);


Din = (UInt16)(Din & 0x3FF);

Vin = (double)(Din * LSB);


String sVIN = Vin.ToString("0.00");
textBox1.Text = sVIN;
}
}
}

private void stopADC_Click(object sender, EventArgs e)


{
if (channelA.Checked && channelB.Checked && channelC.Checked)
{
thEnabled = false;
stopADC.Enabled = false;
startADC.Enabled = true;
}
}
}
}

The source code of the Arduino Zero application is shown in Listing 14.

Listing 14.

int i1;
int tmp, Din;

double vDAC = 0.15; // test voltage from DAC (A0) to A2 (ADC)


double LSB = 3.33/1024; // LSB for DAC
int Dout; // binary code for DAC

void setup() {
for (i1 = 0; i1 < 10; i1++)
pinMode(i1, OUTPUT); // 10 data bits are outputs

pinMode(10, OUTPUT);

75
analogReadResolution(10);
analogWriteResolution(10);
digitalWrite(10, LOW);
}

void loop()
{
vDAC = vDAC + 0.2;
if (vDAC >= 3.2) vDAC = 0.1;
Dout = (int)(vDAC / LSB);
analogWrite(A0, Dout);
delay(5);

Din = analogRead(A2);
setOutput(Din);
digitalWrite(10, HIGH); // the high edge of a strobe arrives at bit 2 of
channel C on the FT4232H board
delay(20);
digitalWrite(10, LOW);
delay(3000);
}

void setOutput(int Din)


{
for (i1 = 0; i1 < 10; i1++)
digitalWrite(i1, LOW); // Clear all 10 outputs
tmp = 0;
for (i1 = 0; i1 < 10; i1++)
{
tmp = (Din >> i1) & 0x1;
digitalWrite(i1, tmp);
}
}

In this code, we use the digital-to-analog converter of the Arduino Zero


(output A0) for generating analog voltage used for testing channel A2 of
the analog-to-digital converter. In this application, we should connect the
DAC output (pin A0) with ADC input (pin A2). The analog voltage on pin
A2 will be incremented every 3 s by 0.2V until it reaches 3.2V. Then the

76
DAC output becomes 0.1V and the loop repeats.

The window of the running C# .NET application is shown in Fig.22.

Fig.22

Example 9
This example illustrates data transfer between the FT4232H and Arduino
Zero boards using UART. In this project, the data obtained from channel
A2 of the analog-to-digital converter of the Arduino Zero is transferred to
the C# .NET application through channel A of FT4232H configured as
UART. The circuit diagram of the project is shown in Fig.23.

77
Fig.23

In this circuit, the data is transferred from pin 1 (TX) of the Arduino Zero
to pin 3 (RX) of the FT4232H board.
The test signal to channel A2 of the analog-to-digital converter is taken
from the wiper of potentiometer R1.
The main form of the C# .NET application is shown in Fig.24.

78
Fig.24

The C# .NET source code is shown in Listing 15.

Listing 15.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using FTD2XX_NET;

namespace FT4232H_UART_Example_1
{
public partial class Form1 : Form
{
public uint baudRate = 115200; // the Baud Rate value

public UInt32 numBytesAvailable = 0;


public UInt32 numBytesRead = 0;
public string readData;
public bool Paused = false;

public FTDI gpio = new FTDI();


public UInt32 ftdiDeviceCount;
public FTDI.FT_STATUS ftStatus;
public FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList;

public Thread thread1;

public Form1()
{
InitializeComponent();
}

79
private void Form1_Load(object sender, EventArgs e)
{
ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;

// Determine the number of FTDI devices connected to the machine


ftStatus = gpio.GetNumberOfDevices(ref ftdiDeviceCount);
// Check status
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// If no devices available, return


if (ftdiDeviceCount == 0)
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Allocate storage for device info list


ftdiDeviceList = new
FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

// Populate our device list


ftStatus = gpio.GetDeviceList(ftdiDeviceList);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() + "\r");

80
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r\r");
}
}

richTextBox1.AppendText("Channel A is being configured as


UART...\r\r");
// Open channel (device) A using its serial number
ftStatus = gpio.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Set up device data parameters


// Set Baud rate
ftStatus = gpio.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Set data characteristics - Data bits, Stop bits, Parity


ftStatus =

81
gpio.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8,

FTDI.FT_STOP_BITS.FT_STOP_BITS_1,

FTDI.FT_PARITY.FT_PARITY_NONE);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set data characteristics (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Set read timeout to 30 seconds, write timeout to infinite


ftStatus = gpio.SetTimeouts(30000, 0);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set timeouts (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

ftStatus = gpio.SetLatency(10);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set a latency timer (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

thread1 = new Thread(threadProc);


thread1.Start();
rxStart.Enabled = false;
rxStop.Enabled = true;
}

private void Form1_FormClosing(object sender,


FormClosingEventArgs e)

82
{
if (gpio.IsOpen) gpio.Close();
thread1.Abort();
}

public void threadProc()


{
while (true)
{
// Check the amount of data we want to read
if (!Paused)
{
ftStatus = gpio.GetRxBytesAvailable(ref numBytesAvailable);
if (numBytesAvailable > 0)
{
ftStatus = gpio.Read(out readData, numBytesAvailable, ref
numBytesRead);
richTextBox1.AppendText(readData);
numBytesAvailable = 0;
}
}
}
}
private void rxStart_Click(object sender, EventArgs e)
{
if (Paused) Paused = false; // resuming the thread proc
rxStart.Enabled = false;
rxStop.Enabled = true;
Thread.Sleep(100);
richTextBox1.AppendText("Resuming UART...\r");
}

private void rxStop_Click(object sender, EventArgs e)


{
if (!Paused) Paused = true; // pausing the thread proc
rxStart.Enabled = true;
rxStop.Enabled = false;
Thread.Sleep(100);
richTextBox1.AppendText("Stopping UART...\r");
}

83
}
}

In this code, channel A of the FT4232H device is configured as UART.


Note that in this case we dont need to configure the operation mode
by invoking a SetBitMode() function. As in the previous examples, we
should open the selected channel using its serial number:

ftStatus = gpio.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);

If the operation was succeeded, we can set the parameters of a


communication channel. The baud rate is configured by the function:

ftStatus = gpio.SetBaudRate(baudRate);

The following statement sets number of data bits, stop bits and parity:

ftStatus =
gpio.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8,

FTDI.FT_STOP_BITS.FT_STOP_BITS_1,

FTDI.FT_PARITY.FT_PARITY_NONE);

We should also set timeout for the read operation (in this case, we selected
30 s):

ftStatus = gpio.SetTimeouts(30000, 0);

The statement

ftStatus = gpio.SetLatency(10);

is optional. It allows to set the latency timer value that determines the
receive buffer timeout that is used to flush remaining data from the receive
buffer. For FT4232H, this timeout is programmable and can be set at 1 mS
intervals between 2 mS and 255 mS. This allows the device to be better
optimized for protocols requiring faster response times from short data
packets.

84
To operate with UART, the program code creates the separate thread
thread1:

thread1 = new Thread(threadProc);


thread1.Start();

Reading data from UART receive buffers is performed within the


threadProc() procedure. Here the statement

ftStatus = gpio.GetRxBytesAvailable(ref numBytesAvailable);

checks if some data bytes are available in the receive buffer. If yes, the
data is stored in the to the string readData whose value is then output in
the richTextBox1. These operations are performed by the following
sequence:

if (numBytesAvailable > 0)
{
ftStatus = gpio.Read(out readData, numBytesAvailable, ref
numBytesRead);
richTextBox1.AppendText(readData);
numBytesAvailable = 0;
}

The variable Paused is used to start/stop reading UART.

The source code of the Arduino Zero application is shown in Listing 16.

Listing 16.

int Din, Dout;


double vADC2, vDAC;
double LSB = 3.33/1024;

void setup() {
// put your setup code here, to run once:
Serial5.begin(115200);
analogReadResolution(10);
analogWriteResolution(10);
vDAC = 0.15; // the initial tes DAC value

85
}

void loop() {
// put your main code here, to run repeatedly:
vDAC = vDAC + 0.1;
if (vDAC >= 3.0) vDAC = 0.15;
Dout = vDAC/LSB;
analogWrite(A0, Dout);
Din = analogRead(A2);
vADC2 = (double)(LSB * Din);
Serial5.print("Voltage on channel A2: ");
Serial5.print(vADC2, 3);
Serial5.println("V");
delay(5000);
}

In this code, the test voltage (variable vDAC) from the digital-to-analog
converter of the Arduino Zero (pin A0) is fed to channel A2 of the analog-
to-digital converter (pin A2). After A/D conversion is complete, the binary
code corresponding to the input voltage on pin A2 is stored in the variable
Din. Then the value of the input voltage in double format (variable
vADC2) is transferred via the Serial5 interface to the FT4232H board.
The loop repeats every 5 s with the test voltage (vDAC) incremented by
0.1 V. When vDAC reaches 3.0V, it starts incrementing from 0.15V.

The window of the running C# .NET application is shown in Fig.25.

86
Fig.25

Example 10
This example illustrates how to transfer data from the FT4232H board to
the Arduino Zero via UART. In this project, the Arduino on-board LED
(pin 13) is controlled by a command sent by C# .NET application through
UART (channel A of FT4232H).
The circuit diagram of the project is shown in Fig.26.

87
Fig.26

In this circuit, the data is transferred from pin 4 (TX line of channel A) of
the FT4232H board to pin 0 (RX) of the Arduino Zero.

The main form of the C# .NET application running in Windows is shown


in Fig.27.

Fig.27

88
In this application, the CheckBox component is used for toggling the
Arduino on-board LED.

The C# .NET source code of the Windows application is shown in Listing


17.

Listing 17.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using FTD2XX_NET;

namespace FT4232H_UART_Example_2
{
public partial class Form1 : Form
{
public uint baudRate = 9600; // the Baud Rate value

public string dataToWrite = "0";


public UInt32 numBytesWritten = 0;

public FTDI uartA = new FTDI();

public UInt32 ftdiDeviceCount;


public FTDI.FT_STATUS ftStatus;

public FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList;

public Form1()
{
InitializeComponent();
}

89
private void Form1_Load(object sender, EventArgs e)
{
ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;

// Determine the number of FTDI devices connected to the machine


ftStatus = uartA.GetNumberOfDevices(ref ftdiDeviceCount);
// Check status
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// If no devices available, exit


if (ftdiDeviceCount == 0)
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Allocate storage for device info list


ftdiDeviceList = new
FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

// Populate our device list


ftStatus = uartA.GetDeviceList(ftdiDeviceList);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() + "\r");

90
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r\r");
}
}

// Configuring channel A as UART

richTextBox1.AppendText("Channel A is being configured as


UART...\r\r");
// Open channel (device) A using its serial number

ftStatus =
uartA.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device A (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Set up data parameters for channel A


// Set Baud rate
ftStatus = uartA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();

91
}

// Set data characteristics - Data bits, Stop bits, Parity


ftStatus =
uartA.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8,

FTDI.FT_STOP_BITS.FT_STOP_BITS_1,

FTDI.FT_PARITY.FT_PARITY_NONE);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set data characteristics (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

// Set read timeout to 5 seconds, write timeout to infinite


ftStatus = uartA.SetTimeouts(5000, 0);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set timeouts (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}

ftStatus = uartA.SetLatency(10);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set a latency timer (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
checkBox1.Checked = false;
}

private void Form1_FormClosing(object sender,


FormClosingEventArgs e)

92
{
if (uartA.IsOpen) uartA.Close();
}

private void checkBox1_Click(object sender, EventArgs e)


{
if (checkBox1.Checked)
{
checkBox1.Text = "LED is ON";
dataToWrite = "1";
}
else
{
checkBox1.Text = "LED is OFF";
dataToWrite = "0";
}
ftStatus = uartA.Write(dataToWrite, dataToWrite.Length, ref
numBytesWritten);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to write to device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
}
}

In this code, channel A of the FT4232H device is configured as UART. As


in the previous examples, we use the following statements:

ftStatus = gpio.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
...
ftStatus = gpio.SetBaudRate(baudRate);
...
ftStatus =
gpio.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8,

FTDI.FT_STOP_BITS.FT_STOP_BITS_1,

93
FTDI.FT_PARITY.FT_PARITY_NONE);
...
ftStatus = gpio.SetTimeouts(5000, 0);
...
ftStatus = gpio.SetLatency(10);

Driving the Arduino LED is performed by the checkBox1_Click event


handler code. If checkBox1 component is checked, the variable
dataToWrite is assigned 1; conversely, if checkBox1 is unchecked,
dataToWrite is assigned 0. Then the value held in dataToWrite is
written to channel A. These operations are performed by the following
sequence:

if (checkBox1.Checked)
{
checkBox1.Text = "LED is ON";
dataToWrite = "1";
}
else
{
checkBox1.Text = "LED is OFF";
dataToWrite = "0";
}
ftStatus = uartA.Write(dataToWrite, dataToWrite.Length, ref
numBytesWritten);
...

The source code of the Arduino Zero application is shown in Listing 18.

Listing 18.

void setup() {
// put your setup code here, to run once:
Serial5.begin(9600);
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
}

94
void loop() {
// put your main code here, to run repeatedly:
while (Serial5.available() > 0)
{
// look for the next valid integer in the incoming serial stream:
int bRcv = Serial5.parseInt();
if (bRcv == 1) digitalWrite(13, HIGH);
else if (bRcv == 0) digitalWrite(13, LOW);
}
}

In this code, the command line from the FT4232H board is received
through the Serial5 interface of the Arduino Zero. The program code
parses the command line and drive the on-board LED ON/OFF depending
on the value taken from UART (variable bRcv).

The window of the running C# .NET application is shown in Fig.28.

Fig.28

In this window, the checkedBox component is selected, therefore the


Arduino on-board LED is driven ON.

This book was downloaded from AvaxHome!

95
Visit my blog for more new books:

www.avxhm.se/blogs/AlenMiler

96
Table des Matires
Introduction 4
Disclaimer 4
Using the FT4232H device in measurement systems 4
Software 6
Hardware 7
Example 1 9
Example 2 18
Example 3 21
Example 4 31
Example 5 34
Example 6 45
Example 7 55
Example 8 65
Example 9 77
Example 10 87

97